useId접근성 속성에 전달될 수 있는 고유 ID를 생성하기 위한 React Hook
const id = useId()
최상위 수준에서 useId를 호출하여 고유 ID를 생성할수 있어요
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
useId매개변수를 사용하지 않아요.
useId이 특정 구성 요소의 이 특정 호출과 관련된 고유 ID 문자열을 반환해요.
함정
useId목록에 키를 생성하기 위해 호출하지 말것
최상위 수준에서 useId를 호출하여 고유 ID를 생성
import { useId } from 'react';
function PasswordField() {
const passwordHintId = useId();
// ...
그런 다음 생성된 ID를 다른 속성에 전달할 수 있어요
<>
<input type="password" aria-describedby={passwordHintId} />
<p id={passwordHintId}>
</>
HTML 접근성 속성을 사용하면 aria-describedby두 개의 태그가 서로 관련되어 있음을 지정할 수 있어요. 예를 들어, 입력과 같은 요소가 단락과 같은 다른 요소에 의해 설명되도록 지정할 수 있어요.
<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를 부여해야 하는 경우 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도 유지가 되어 훨씬 안정적이에요.
단일 페이지에서 여러 개의 독립적인 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:
동일한 페이지에 여러 개의 독립적인 React 앱을 렌더링하고 이러한 앱 중 일부가 서버에서 렌더링되는 경우 클라이언트 측에서 hydrateRoot 호출에 전달하는 identifierPrefix가 renderToPipeableStream과 같은 서버 API에 전달하는 identifierPrefix와 동일한지 확인해야 한다.
import { renderToPipeableStream } from 'react-dom/server';
const { pipe } = renderToPipeableStream(
<App />,
{ identifierPrefix: 'react-app1' }
);
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(
domNode,
reactNode,
{ identifierPrefix: 'react-app1' }
);
페이지에 React 앱이 하나만 있는 경우 identifierPrefix를 전달할 필요가 없어요