React에서 일반적으로 사용할 수 없는 prop이 몇 가지 있다.
그 중 ref prop도 HTML Element 접근이라는 특수한 용도로 사용되기 때문에 일반적인 prop으로 사용할 수 없다.
React Component에서 ref prop을 사용하려면 forwardRef() 라는 함수를 사용해야 한다. React Component를 forwardRef() 함수로 감싸주면, 컴포넌트 함수는 2번째 매개변수를 갖게 되는데 이를 통해 ref prop을 넘길 수 있다.
원래 prop으로 표현할 수 있으면, ref를 써서는 안 된다.
ref는 필수적인 경우에만 사용해야 한다.
prop으로 표현할 수 없는, scrolling to a node, focusing a node, triggering an animation, selecting text 등이다.
기본은, 각 컴포넌트 안에 있는 DOM nodes는 바깥에서 접근이 안 되는, private한 값이다. 하지만 부모에게 이 DOM node 를 노출하는 것이 유용할 때도 있다. 그러면 반드시 그 자식 컴포넌트를 forwardRef로 감싸야 한다.
그러면, 이제 두번째 인자로 ref를 받을 수 있고, 이걸 자식 컴포넌트 내의 DOM node에 전달할 수 있다.
그러면 부모 컴포넌트는 Form이고, 자식 컴포넌트는 MyInput일 때, Form 컴포넌트가 <input> DOM node 에 접근해서 focus()도 호출할 수 있다. 이건 단순히 low-level 일 때 예를 들어, buttons, text inputs 정도일 때 사용해야지 application-level components(아바타, 댓글 등) 일 때 사용하면 안 된다. 나중에 디버깅이 어렵다.
ref를DOM node에 전달할 수도 있고, 또다시component에 전달할 수도 있다.
imperative handle이라고 하는 것은custom object를 말하는데, 더 제한된 묶음의 메서드를 말한다. 이걸 하려면 먼저 분리된ref를 먼저 정의해서 DOM node 를 따로 뺀 다음에, 그것을 기반으로 여러개의 메서드를 정의해야 한다.
useImperativeHandle을 사용해서 ref를 받을 수 있다.
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
}};
}, []);
return <input {...props} ref={inputRef} />;
});
이런식으로 하면, MyInput에서 ref를 받을 때, DOM node 대신에 { focus, scrollIntoView }를 받을 수가 있다.
이것의 장점은, 받는 데이터의 형태(포맷)가 정해진다는 것이다.