리액트에서 제공하는 훅을 사용하다 보면 훅을 제어하거나 재사용하기 위해 두번째 인자로 의존성 배열을 받는 경우가 있습니다.
만약, 해당 훅 내부에서 사용하는 상태나 함수가 있음에도 의존성 배열에 넣지 않는다면 ESLint와 같은 린터가 아래와 같은 경고 표시를 띄우곤 합니다.
리액트는 VDOM을 사용해 DOM의 변경사항을 추적하고 최적화하기 때문에
DOM에 접근할 일이 있다면 직접 접근하지 말고 ref객체를 통해 DOM에 접근할것을 권장하고 있습니다.
그렇다면 useCallback
, useMemo
, useEffect
등과 같은 훅을 사용할 때 의존성 배열에 useRef
를 통해 생성한 ref객체를 넣어야할까요?
결론부터 말하자면 넣지 않아도 됩니다.
왜 그런지에 대해서 이해하려면 먼저 ref객체의 특징에 대해 알아야 합니다.
useRef(createRef)
를 통해 생성한 ref객체는 동적 객체로서 초기값을 할당하거나 DOM 요소와 연결하는 목적으로 사용되며 이들은 ref객체의 current속성 값이 됩니다.
useRef
훅은 값이 변경되었을 때 리렌더링을 유발하지 않는다는점을 제외하면 useState
와 매우 흡사합니다.
즉, current속성 값이 변경되더라도 리렌더링을 발생시키지 않도록 내부적으로 설계되어 있습니다.
만약, ref의 값을 DOM요소가 아닌 String
, Number
와 같은 원시 타입을 사용한다고 하더라도 상태 변경과 같은 외부 요인에 의해 컴포넌트가 리렌더링 되지 않는 한 컴포넌트 내부에서는 ref객체의 변경된 값을 감지할 수 없습니다.
위 예시는 숫자를 ref와 state의 값으로 각각 할당하고 카운트를 상승시켰을 때 어떤 차이가 발생하는지 잘 보여줍니다.
오른쪽 박스는 state를 사용했기 때문에 버튼을 누를 때마다 컴포넌트가 상태 변경을 감지하여 변경된 값을 즉시 보여줍니다.
반면, ref를 사용한 예시인 왼쪽 박스는 COUNT UP 버튼을 누르면 실제로 카운트가 올라가지만 컴포넌트는 이를 감지하지 못합니다.
그래서, 카운트가 아닌 다른 상태를 변경하는 re-render
라는 버튼을 눌렀을 때 내부 상태가 변경되면서 리렌더링이 발생해야만 컴포넌트가 변경된 값을 감지해 숫자가 변경되는 모습을 볼 수 있습니다.
그래서 ref.current를 deps에 넣어보면 넣을 필요가 없다고 ESLint에서도 표시해주고 있습니다.
만약 컴포넌트가 값 변경을 감지해야 한다면 ref가 아닌 state와 같이 리렌더링을 발생시키는 훅을 사용해야 합니다.
callback ref는 리액트 공식문서에서도 나와있는 기법으로 ref를 정의할 때 useRef
훅을 사용하는게 아닌 콜백함수를 정의하는것을 말합니다.
- callback ref 적용
const ref = (elem) => { if (elem) { // Some Code...... } }; return ( // 아래 div 요소가 마운트 및 언마운트 될 때 콜백 ref가 호출됨. <div ref={ref}> </div> );
이는 ref를 설정해놓은 DOM 또는 컴포넌트가 마운트 및 언마운트 될 때마다 해당 콜백함수가 호출되며 마운트되었다면 해당 요소를, 언마운트되었다면 null
을 인자로 전달받습니다.
보통 ref가 설정된 요소의 크기를 측정하거나 렌더링 됬는지의 여부를 파악할 때 많이 사용됩니다.
ref는 리렌더링을 유발시키지 않기 때문에 의존성배열에 넣는다해도 컴포넌트 내부에서 변경을 감지하지 못한다.
따라서, 해당 값이 변경된것을 컴포넌트가 감지하길 원한다면 state와 같이 리렌더링을 발생시키는 훅을 사용해주어야 한다.
또한, ref대상 요소의 크기 측정, 렌더링 여부를 파악하고 싶다면 callback ref를 사용하면 된다.