useRef는 언제 사용하나요?
DOM 조작 및 접근: 특정 DOM 요소에 접근하거나 조작할 때 사용
값의 변경 추적: 값이 변경되더라도 리렌더링되지 않는다는 특징을 활용, 값이 유지할 때 사용
const ref = React.useRef(null);
<div ref={ref}>{children}</div>;
useRef
를 통해 렌더링 이후 해당 DOM 노드를 참조하고, 컴포넌트가 언마운트될 때 다시 null로 설정됩니다.
렌더링 이후 특정 DOM 요소에 접근하기: useEffect와 callback ref
useEffect를 사용한 경우
useEffect
를 통해 렌더링 이후 특정 DOM 요소에 접근할 수 있습니다.
const ref = React.useRef(null);
React.useEffect(() => {
if (ref.current) {
ref.current.focus();
}
}, []);
<input ref={ref} />;
이 방법은 초기 렌더링 이후 특정 작업을 수행하는 데 유용합니다. 그러나 렌더링 타이밍이나 조건부 렌더링이 있는 경우 useEffect
의 실행 시점과 ref
의 상태가 맞지 않을 수 있습니다.
복잡한 상황에서의 문제점: 조건부 렌더링
렌더링이 지연되거나 특정 조건에 따라 DOM 요소가 생성될 때, useEffect
와 useRef
조합은 예상치 못한 동작을 할 수 있습니다.
function App() {
const ref = React.useRef(null);
React.useEffect(() => {
// 🚨 이 시점에 ref.current는 항상 null입니다.
ref.current?.focus();
}, []);
return <Form ref={ref} />;
}
const Form = React.forwardRef((props, ref) => {
const [show, setShow] = React.useState(false);
return (
<div>
<button type="button" onClick={() => setShow(true)}>
Show
</button>
{/* 조건부 렌더링: 이때 ref는 비어있습니다. */}
{show && <input ref={ref} />}
</div>
);
});
위와 같은 상황에서는 useEffect
가 실행되는 시점에 ref
가 아직 설정되지 않아 원하는 동작을 보장할 수 없습니다.
callback ref: useEffect 없이 DOM 요소에 안전하게 접근하기
callback ref
는 useEffect
없이 DOM 요소에 직접적으로 접근할 수 있는 방법입니다. 이 방식은 DOM 노드가 렌더링되었을 때 호출되며, DOM의 생명주기에 바인딩됩니다.
const ref = React.useCallback((node) => {
if (node) {
node.focus();
}
}, []);
return <input ref={ref} />;
callback ref
는 useRef
의 대체 방법으로, DOM이 렌더링될 때마다 실행되기 때문에 useEffect
의 실행 타이밍 문제를 해결할 수 있습니다.
useCallback
을 사용하여 함수 참조를 유지함으로써 불필요한 렌더링을 방지할 수 있습니다.
Ref Props의 다형성
React의 ref
는 객체뿐만 아니라 함수도 전달할 수 있습니다. 이는 React의 ref 시스템이 매우 유연함을 보여줍니다.
type Ref<T> = RefCallback<T> | RefObject<T> | null;
useRef
는 단순히 syntactic sugar에 불과합니다.
언제 callback ref를 고려해야 할까?
렌더링 이후 DOM 노드와 상호작용해야 할 때 성급하게 useRef + useEffect
를 사용하는 대신 callback ref
를 사용하여 불필요한 문제를 예방할 수 있습니다.
특히 조건부 렌더링이나 렌더링 타이밍 이슈가 있을 때 callback ref
는 효과적인 대안이 될 수 있습니다.
참고 자료
tkdodo’s blog - Avoiding useEffect with callback refs
'개발 > 기타' 카테고리의 다른 글
설계자와 이해당사자: 공감과 소통의 중요성 (1) | 2024.09.28 |
---|---|
TypeScript Deep-Dive: 부분 타입 추론 (0) | 2023.09.06 |
논리적 가능성과 현실적 가능성 사이에서 협력하기 (0) | 2023.09.06 |
Avoid Hasty Abstractions(AHA) (0) | 2023.08.28 |
shadcn/ui (0) | 2023.08.24 |