useId()
useId() 훅을 사용하면 컴포넌트가 렌더링될 때 마다 고유한 id 를 생성하여 반환해준다. 따라서, 이를 활용한다면, 같은 컴포넌트를 동일한 부모 컴포넌트에서 재사용하는 경우 서로 id 값이 같아서 충돌이 날 수 있는 문제를 사전에 예방할 수 있다.
예를 들어 유저의 이메일을 입력 받은 동일한 컴포넌트를 하나의 부모 컴포넌트에서 여러 번 재사용하는 경우를 가정해보자.
// 이메일을 입력받는 자식 컴포넌트
import { useState } from 'react';
function EmailInput({ onChange }) {
const [email, setEmail] = useState('');
const handleInputChange = (e) => {
const { value } = e.target;
setEmail(value);
onChange(value); // 부모 컴포넌트에 이메일 값 전달
};
return (
<div>
<label htmlFor="email">이메일:</label>
<input
type="email"
id="email"
name="email"
value={email}
onChange={handleInputChange}
placeholder="이메일을 입력하세요"
/>
</div>
);
}
export default EmailInput;
이 경우 부모 컴포넌트에서 동일한 id 를 사용하여 컴포넌트를 렌더링하게 되면 예기치 못한 id 충돌 문제가 발생할 수 있다. 즉, 서로 동일한 id 를 가지고 있기 때문에, 만일 두 번째 이메일 Input 의 라벨을 클릭하더라도 첫 번째 input 에 포커스가 생성되는 문제가 있을 수 있다.
import { useState } from 'react';
import EmailInput from './EmailInput';
function App() {
const [userEmail1, setUserEmail1] = useState('');
const [userEmail2, setUserEmail2] = useState('');
const handleEmailChange1 = (email) => {
setUserEmail1(email);
};
const handleEmailChange2 = (email) => {
setUserEmail2(email);
};
return (
<section>
<h1> 테스트 입력 페이지 </h1>
<div>
<h2>메인 이메일</h2>
<EmailInput onChange={handleEmailChange1} />
<p>현재 입력된 이메일: {userEmail1}</p>
</div>
<div>
<h2>서브 이메일</h2>
<EmailInput onChange={handleEmailChange2} />
<p>현재 입력된 이메일: {userEmail2}</p>
</div>
</section>
);
}
export default App;
이러한 문제를 해결하기 위해서는 각 id 마다 고유한 값을 부여해야 하는데, 이를 위해서 아래와 같이 props 로 전달할 수도 있다.
<EmailInput onChange={handleEmailChange1} id="main"/>
<EmailInput onChange={handleEmailChange2} id="sub" />
하지만, 리액트에서는 이러한 하드 코딩된 값을 사용하는 것을 권장하지 않고 있으며, 이를 개선하기 위한 훅으로 이 포스트의 주제인 userId() 훅을 v18 이후에 출시하였다.
useId() 훅을 사용한 고유 id 생성하기
우선, useId() 훅으로 생성된 id 값은 컴포넌트를 렌더링할 때 마다 새롭게 생성된다. 즉, 이 말은 동일한 컴포넌트라도 렌더링 되는 시점에 서로 다른 고유한 id 값을 생성해 줄 수 있다는 말이다. 따라서 이를 활용하여 다음과 같이 로직을 작성해보자
아래와 같이 작성하면, useId() 훅을 통해서 생성된 값이 id 변수에 할당되는데, 이 id 는 재사용된 컴포넌트 마다 다른 값을 생성하기 때문에, 같은 컴포넌트 끼리 id 가 충돌하는 문제를 해결할 수 있게 된다.
참고로, useId 는 리액트 훅이므로 컴포넌트 최상단에서 import 하고 컴포넌트 함수 상단에서 호출해야 한다. |
import React from 'react';
import {useId} from 'react'; // useId 훅을 가져옴
function EmailInput({ onChange }) {
const id = useId(); // 고유한 ID 생성
const handleInputChange = (e) => {
const { value } = e.target;
onChange(value);
};
return (
<div>
<label htmlFor={id + 'email'}>이메일:</label>
<input
type="email"
id={id + 'email'}
name="email"
onChange={handleInputChange}
placeholder="이메일을 입력하세요"
/>
</div>
);
}
export default EmailInput;
추가적인 내용은 공식문서로
개인적으로 공식문서는 꼭 읽어보기를 권장한다. 특히, useId 를 사용하는 경우 얻을 수 있는 이점과 id++ 가 같은 증분이 되는 방식이 왜 리액트에서는 권장되지 않는 방식인지에 대한 내용도 소개되어 있다. 이를 기반으로 생각해본다면, 우리가 배열을 map 고차함수를 사용하여 렌더링할 때 key 에 index 를 바인딩하는 것을 권장하지 않는지에 대해서도 깊이 생각해 볼 수 있다고 본다.
https://react.dev/reference/react/useId
'리액트' 카테고리의 다른 글
[React] 테스팅의 친구, userEvent 이해하기 with 예제 코드 (0) | 2024.03.10 |
---|---|
[React] React 혹은 Next 에서 forwardRef 타입에 대한 참조를 확인하는 방법 및 지정법 (0) | 2024.02.18 |
[개인 정리]프론트엔드 상태관리 라이브러리 비교(redux, recoil, zustand) (0) | 2023.12.27 |
[React lib] 리액트에서 유효성 검사 라이브러리 4가지 간략 소개 및 링크 정리 (0) | 2023.12.15 |
[react ] Jotai, Recoil, Zustand, Valtio | Jotai (1) (1) | 2023.12.10 |