[TIL] React Hooks - useId

대빵·2023년 11월 20일
1

useId접근성 속성에 전달될 수 있는 고유 ID를 생성하기 위한 React Hook

const id = useId()

참조

useId()

최상위 수준에서 useId를 호출하여 고유 ID를 생성할수 있어요

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  // ...

매개변수

useId매개변수를 사용하지 않아요.

보고

useId이 특정 구성 요소의 이 특정 호출과 관련된 고유 ID 문자열을 반환해요.

주의사항

  • useIdHook이므로 컴포넌트의 최상위 수준 이나 자체 Hook에서만 호출할 수 있습니다. 루프나 조건 내에서는 호출할 수 없습니다. 필요한 경우 새 구성 요소를 추출하고 상태를 해당 구성 요소로 옮깁니다.
  • useId 목록에서 키를 생성하는 데 사용해서는 안 되요.키는 데이터에서 생성해야 되요.

용법

함정

useId목록에 키를 생성하기 위해 호출하지 말것키는 데이터에서 생성해야 되요

접근성 속성에 대한 고유 ID 생성

최상위 수준에서 useId를 호출하여 고유 ID를 생성

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  // ...

그런 다음 생성된 ID를 다른 속성에 전달할 수 있어요

<>
  <input type="password" aria-describedby={passwordHintId} />
  <p id={passwordHintId}>
</>

예제

HTML 접근성 속성을 사용하면 aria-describedby두 개의 태그가 서로 관련되어 있음을 지정할 수 있어요. 예를 들어, 입력과 같은 요소가 단락과 같은 다른 요소에 의해 설명되도록 지정할 수 있어요.

HTML

<label>
  Password:
  <input
    type="password"
    aria-describedby="password-hint"
  />
</label>
<p id="password-hint">
  The password should contain at least 18 characters
</p>

그러나 이와 같은 ID를 하드코딩하는 것은 React에서 좋은 습관이 아니에요. 구성 요소는 페이지에서 두 번 이상 렌더링될 수 있지만 ID는 고유해야 해요. ID를 하드코딩하는 대신 useId로 고유 ID를 생성하세요.

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

PasswordField이제 화면에 여러 번 나타나 더라도 생성된 ID가 충돌하지 않아요

import { useId } from 'react';

function PasswordField() {
  const passwordHintId = useId();
  return (
    <>
      <label>
        Password:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        The password should contain at least 18 characters
      </p>
    </>
  );
}

export default function App() {
  return (
    <>
      <h2>Choose password</h2>
      <PasswordField />
      <h2>Confirm password</h2>
      <PasswordField />
    </>
  );
}

함정

서버 렌더링의 경우 useId를 사용하려면 서버와 클라이언트에서 동일한 컴포넌트 트리가 필요해요. 서버와 클라이언트에서 렌더링하는 트리가 정확히 일치하지 않으면 생성된 ID가 일치하지 않아요.

여러 관련 요소에 대한 ID 생성

관련된 여러 요소에 ID를 부여해야 하는 경우 useId를 호출하여 해당 요소들의 공통 접두사를 생성할 수 있어요

import { useId } from 'react';

export default function Form() {
  const id = useId();
  
  return (
    <form>
      <label htmlFor={`${id}-firstName`}>First Name:</label>
      <input id={`${id}-firstName`} type="text" />
      <hr />
      <label htmlFor={`${id}-lastName`}>Last Name:</label>
      <input id={`${id}-lastName`} type="text" />
    </form>
  );
}

이렇게 하면 고유 ID가 필요한 모든 요소에 대해 useId를 호출하지 않아도 되요

요소에 접근

useId는 id에다가 쌍점을 넣어줌으로서 리액트 개발자로서 조금 더 나은 코드를 작성할 수 있게 도와주는 역할을 해요.

import { useId, useRef } from 'react';

export default function Input() {
  const id = useId();
  const ref = useRef();
  
  useEffect(() => {
    const element = ref.current;
    }, []);
  
  return (
    <form>
      <label htmlFor={id}>검색</label>
      <input id={id} ref={ref} />
    </form>
  );
}

컴포넌트에 랜더링이 발생해도 아이디가 그대로 유지되 폼 요소에있는 id도 유지가 되어 훨씬 안정적이에요.

생성된 모든 ID에 대한 공유 접두사 지정

단일 페이지에서 여러 개의 독립적인 React 애플리케이션을 렌더링하는 경우, createRoot, hydrateRoot 호출에 identifierPrefix를 옵션으로 전달할 것. 이렇게 하면 useId로 생성된 모든 식별자가 지정한 고유한 접두사로 시작하므로 서로 다른 두 앱에서 생성된 ID가 충돌하지 않아요.

import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';

const root1 = createRoot(document.getElementById('root1'), {
  identifierPrefix: 'my-first-app-'
});
root1.render(<App />);

const root2 = createRoot(document.getElementById('root2'), {
  identifierPrefix: 'my-second-app-'
});
root2.render(<App />);
             
// console.log
생성된 식별자: :my-first-app-r0:
생성된 식별자: :my-second-app-r1:

클라이언트와 서버에서 동일한 ID 접두사 사용

동일한 페이지에 여러 개의 독립적인 React 앱을 렌더링하고 이러한 앱 중 일부가 서버에서 렌더링되는 경우 클라이언트 측에서 hydrateRoot 호출에 전달하는 identifierPrefix가 renderToPipeableStream과 같은 서버 API에 전달하는 identifierPrefix와 동일한지 확인해야 한다.

Server

import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(
  <App />,
  { identifierPrefix: 'react-app1' }
);

Client

import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = hydrateRoot(
  domNode,
  reactNode,
  { identifierPrefix: 'react-app1' }
);

페이지에 React 앱이 하나만 있는 경우 identifierPrefix를 전달할 필요가 없어요

0개의 댓글

관련 채용 정보