[TIL #31] null을 return하는 useRef

문제 발생

토이 프로젝트를 진행하면서 Modal을 구현하고 있는데, Modal의 상단 부분의 높이Modal의 하단 부분에 전달하고 싶어서 간단하게 구현해보았다.

우선 상단 높이를 저장할 clientHeight state를 생성하고, 상단 부분의 DOM 높이를 측정하기 위해서 useRef를 통해 상단부분의 ref 값으로 설정했다.
또한 useEffect를 이용해서 topRef(상단 부분)의 높이가 바뀔 때 마다 setState를 호출하도록 작성했다.

문제 코드

const [clientHeight, setClientHeight] = useState(0);
const topRef = useRef();

useEffect(()=>{
  setClientHeight(topRef.current.clientHeight);
},[topRef.current]);

// ...some codes
<ModalTopInfo ref={topRef}/>
<ModalBottomInfo height={clientHeight}/>

위와같이 코드를 작성했는데 ModalBottomInfo로 height 값이 이상하게 전달되는 것 같아서 확인해보니 topRef.currentnull을 return 하고 있었다. 그러니 topRef.current.clientHeight 또한 원하는 값을 return 하지 못하고 있었다.

Ref 타입을 선언을 안해줘서 그런가 싶어서 타입도 설정해봤지만 여전이 null을 return 하길래 비슷한 사례가 있나 싶어서 검색해보니 역시나 동지1 동지2 동지3들이 있었다.

문제 해결

무엇이 문제인고 하니, useRef에 의해 생성된 ref는 컴포넌트 리렌더링을 발생시키지 않는다.

useRef를 통해서 ref가 생성이 되긴 했지만 const topRef = useRef();를 초기값을 null로 초기화했으므로 첫번째 렌더링시 null로 유지된다. 첫번째 렌더링 중ref={topRef}를 통해서 ref 값을 업데이트 하지만, 위에서 말했다 싶이 첫번째 렌더링 이외의 다른 렌더링을 발생시키지는 않기 때문에 ref는 여전히 null 값으로 기억되고 있는 것이다.

만약 업데이트 된 ref 값을 사용하고 싶다면 또 다른 리렌더링을 발생시켜서 ref 값이 바뀌었다는 것을 알려줘야한다.

이에 대한 해답은 React 공식문서에서 찾을 수 있었다. (심지어 내 사례와 똑같이 DOM 높이를 측정하는 예시다!)

문제 해결 코드

const [clientHeight, setClientHeight] = useState(0);
const topRef = useCallback((node)=>{
   if(node !== null){
     setClientHeight(node.getBoundingClientRect().height);
   }
},[]);

// ...some codes
<ModalTopInfo ref={topRef}/>
<ModalBottomInfo height={clientHeight}/>

예시 코드를 보면 useRef를 사용하지 않고 useCallback을 통해 ref를 생성하고 해당 컴포넌트가 마운트, 언마운트 될때 호출되도록 작성되어 있고, 해당 node가 존재하는 경우에만 setClientHeight를 호출하도록 작성되어 있다.

결론

useRef는 값이 변경 되더라도 사용자에게 알리지 않는다 (리렌더링을 발생시키지 않는다)
만약 React가 ref를 DOM node에 연결하거나 분리할 때 특정 코드를 실행하고 싶다면 useCallback을 사용하자!

좋은 웹페이지 즐겨찾기