const refContainer = useRef(initialValue);
useRef
는 .current
프로퍼티가 인수로 전달받은 initialValue
로 초기화된 변형 가능한 ref 객체를 반환함useRef
는 보통 자식에 명령적으로 접근하는데 사용함function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current`는 마운트된 text input element를 가리킴
inputEl.current.focus();
}
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
ref는 DOM을 접근하기 위해서 사용됨
<div ref={myRef} />
를 사용하여 React에 ref 객체를 전달할 경우, React는 그 노드가 변할 때마다 대응되는 DOM 노드를 .current
프로퍼티로 설정함useRef
는 클래스의 인스턴스 필드와 비슷하게 가변값을 유지하는 데 유용함
useRef
가 순수 JavaScript 객체를 생성하기 때문임useRef
는 매번 렌더링 할 때 동일한 ref 객체를 제공함function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
// effect 내부에서 interval을 취소할 경우, useRef를 사용하지 않아도 됨
return () => {
clearInterval(intervalRef.current);
};
});
// 이벤트 핸들러에서 interval을 취소할 경우, useRef를 사용해서 취소할 interval을 기억해놓을 수 있음
function handleCancleClick() {
clearInterval(intervalRef.current);
}
return (
<>
<h1>Timer</h1>
<button onClick={handleCancleClick}>Cancle</button>
</>
);
}
useRef
는 .current
프로퍼티가 변형되도 리렌더링이 발생하지 않음callback ref
를 대신 사용해야 함callbak ref
를 사용함callback ref
는 useRef
와 달리 ref 내용이 변경될 경우 부모 컴포넌트에게 알려주기 때문에, 측정값이 갱신됨function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1>
<h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
useCallback
의 두 번째 인수 dependency 배열로 []
을 전달할 경우, ref callback이 리렌더링 사이에 변하지 않는다는 것을 보장함
위 코드에서 callback ref는 컴포넌트가 마운트되고 해제될때만 호출됨
ResizeObserver
를 사용하거나 관련된 3th party Hook을 사용해야 함위 코드의 로직을 재활용 가능한 Hook으로 추출할 수 있음
function MeasureExample() {
const [rect, ref] = useClientRect();
return (
<>
<h1 ref={ref}>Hello, world</h1>
{rect !== null &&
<h2>The above header is {Math.round(rect.height)} px tall</h2>
}
</>
);
}
function useClientRect() {
const [rect, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
useRef
는 useState
처럼 함수 오버로드를 받아들이지 않으므로, 초기값을 지연 세팅해주는 함수를 만들어서 사용해야 함// bad
function Image(props) {
// 매번 렌더링할 때마다 IntersectionObserver가 생성됨
const ref = useRef(new IntersectionObserver(onIntersect));
}
// good
function Image(props) {
const ref = useRef(null);
// IntersectionObserver는 한번만 지연적으로 생성됨
function getObserver() {
if (ref.current === null) {
ref.current = new IntersectionObserver(onIntersect);
}
return ref.current;
}
// ref.current에 접근 해야할 때 getObserver()를 호출함
}
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
은 ref
를 사용할 때 부모 컴포넌트에 노출된 인스턴스 값을 커스터마이징할 수 있음useImperativeHandle
은 forwardRef
와 같이 사용해야 함useImerativeHandle
의 첫 번째 인자 : 프로퍼티를 부여할 ref
useImerativeHandle
의 두 번째 인자 : ref
객체에 추가하고 싶은 프로퍼티를 정의함function FancyInput(props, ref) {
const inputRef = useRef();
useImpertiveHandle(ref, () => {
reallyFocus: () => {
inputRef.current.focus();
console.log('Being focused!');
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
위 코드에서 <input ref={inputRef} />
를 렌더링하는 부모 컴포넌트 FancyInput는 inputRef.current.reallyFocus를 호출할 수 있음
useImperativeHandle
을 사용할 때의 장점