React에서 forwardRef는 부모 컴포넌트에서 자식 컴포넌 중 하나로 ref를 전달할 수 있는 고차 컴포넌트이다. React 라이브러리가 아닌 외부 라이브러리나 ref가 필요한 DOM 요소를 사용하는 컴포넌트를 만들 때 해당 컴포넌트에 ref를 전달해야 한다. 그러나 ref를 자식 컴포넌트에 직접 전달하려고 하면 React는 '함수 컴포넌트에 ref를 지정할 수 없습니다.'라는 에러를 표시한다.
여기서 forwardRef를 사용할 수 있다. 참조를 받아들이고 자식 컴포넌트로 전달하는 새로운 컴포넌트를 만드는 방법은 아래와 같다. 먼저, forwardRef를 사용하려면 props와 ref라는 두 개의 파라미터를 사용하는 함수 컴포넌트를 만들어야 하고, 컴포넌트에서 ref 파라미터를 사용하여 자식 컴포넌트에 전달해야 한다.
const ChildComponent = React.forwardRef((props, ref) => {
return (
<div ref={ref}>
{props.children}
</div>
)
}
const ParentComponent = () => {
const childRef = React.useRef(null);
React.useEffect(() => {
console.log(childRef.current);
}, []);
return (
<ChildComponent ref={childRef}>
Hello, world!
</ChildComponent>
)
}
여기서 ChildComponent가 forwardRef를 사용하여 정의된다. 그리고 ref를 파라미터로 받아 div 엘리먼트에 전달한다. ParentComponent는 useRef를 사용하여 참조를 만들고 ChildComponent로 전달한다. ParentComponent가 마운트 되면 div 엘리먼트의 ref인 childRef.current 값을 기록한다.
forwardRef를 사용하면 자식 컴포넌트에 추가 객체를 전달할 수도 있다. 객체는 함수 컴포넌트의 props의 파라미터로 전달된다.
const ChildComponent = React.forwardRef((props, ref) => {
const { className, ...rest } = props;
return (
<div className={`child ${className}`} ref={ref} {...rest}>
{props.children}
</div>
)
})
const ParentComponent = () => {
const childRef = React.useRef(null);
return (
<ChildComponent ref={childRef} className="red" tabIndex={0}>
Hello, world!
</ChildComponent>
)
}
요약하면 forwardRef는 부모 컴포넌트에서 자식 컴포넌트로 ref를 전달할 수 있는데, props의 파라미터와 스프레드 연산자를 사용하여 자식 컴포넌트에 props를 전달할 수도 있다.
forwardRef의 또 다른 사용 예시는 컴포넌트 트리의 여러 레벨에 중첩된 컴포넌트에 ref를 전달해야 하는 경우이다. forwardRef가 없으면 모든 중간 컴포넌트를 통해 ref를 전달해야 하기 때문에 무의미한 반복과 오류가 발생하기 쉽다.
const GrandchildComponent = React.forwardRef((props, ref) => {
return (
<div ref={ref}>
{props.children}
</div>
)
})
const ChildComponent = (props) => {
return (
<GrandchildComponent {...props}>
{props.children}
</GrandchildComponent>
)
}
const ParentComponent = () => {
const grandchildRef = React.useRef(null);
React.useEffect(() => {
console.log(grandchildRef.current);
}, []);
return (
<ChildComponent>
<div>Hello, world!</div>
<GrandchildComponent ref={grandchildRef}>
<div>Nested component</div>
</GrandchildComponent>
</ChildComponent>
)
}
여기에서는 GradnchildComponent에 forwardRef를 정의하여 ParentComponent가 컴포넌트 트리에서 여러 레벨에 깊이 중첩되어 있어도 참조를 전달할 수 있게 하는데, 먼저 ChildComponent는 props를 GrandchildComponent로 전달한다. 그리고 ParentComponent는 useRef를 사용하여 ref를 만들고 GrandchildComponent에 props를 전달한다.
ParentComponent가 마운트 되면 GrandchildComponent 내부의 div 엘리먼트인 grandchildRef.current 값을 기록한다. 쉽게 말해서 div 엘리먼트와 'Hello, world!'는 props로만 ChildComponent에 내리고 그 ChildComponent에서 GrandchildComponent로 props를 내린 경우이고, div 엘리먼트와 'Nested component'는 forwardRef를 통해서 GrandchildComponent로 바로 ref와 props를 내린 것이다. 결국 ref는 'Nested component' 문자열을 담은 div 엘리먼트만 적용된다. 이렇게 참조를 특정해서 각각 제어할 수 있게 도와주는 것이다.
- forwardRef 사용 주의점
forwardRef를 사용할 때의 주의점으로 먼저, forwardRef는 코드를 이해하고 유지하기 어렵게 만들 수 있다. 그래서 자주 사용해서는 안된다. 일반적으로 해결 가능한 수준의 컴포넌트 구성인 경우 ref를 전달하기보단 콜백이나 state와 같은 것으로 해결하는 것이 좋다.
둘째로, forwardRef를 사용할 때 잠재적인 성능 영향을 이해하고 있어야 한다. forwardRef는 상위 컴포넌트에서 사용하기 때문에 그 하위 컴포넌트에 간접적으로 성능적 영향을 줄 수 있다. 특히 리스트 항목과 같이 자주 재렌더링되는 컴포넌트에서는 forwardRef를 사용하면 상당한 성능 저하가 발생할 수 있다.
마지막으로 forwardRef가 React에서 ref를 전달하는 유일한 방법이 아니라는 것이다. 자식 컴포넌트가 ref를 통해 부모 컴포넌트에 특정 메소드 집합을 접근할 수 있도록 하는 useImperativeHandle hook을 사용할 수도 있다. 이는 부모 컴포넌트와 자식 컴포넌트 간의 상호 작용을 보다 세밀하게 제어해야 하는 상황에서 유용할 수 있다.
'React' 카테고리의 다른 글
렌더링 된 후 특정 엘리먼트 접근하기 (후처리하기) (0) | 2023.03.23 |
---|---|
Fragments (0) | 2023.03.23 |
Error Boundary (0) | 2023.03.21 |
useEffectEvent (0) | 2023.03.20 |
Context (0) | 2023.03.16 |