[한 주에, 한 주제] callback Ref

김민석·2021년 12월 6일
0
post-thumbnail

React-grid-layout 라이브러리를 이용해서 드래그앤드랍 & 화면 편집 기능이 들어간 화면을 만들었다.

화면에는 여러가지 카드들이 보여지고, 하나의 카드는 시각화된 데이터를 보여주는 화면이다.

해당 화면은 다음의 기능들이 들어가 있었다.

  1. 디스플레이 될 카드들의 선택 (카드 모음에서 넣고 뺄 수 있음)
  2. 카드들의 위치를 드래그앤드랍으로 조정
  3. 카드 크기 Resize
  4. layout을 전역 상태에 저장

이때, Resize 기능을 구현하면서 react의 callback ref를 사용하게 되었다.


내가 구현한 화면은 어찌보면 React-grid-layout이 일반적으로 의도한 사용과 벗어난 부분이 있었다.
라이브러리 자체는 각 카드의 컨텐츠가 반응형으로 구성되어 있어서 resize시 컨텐츠가 알맞게 변형될 수 있게 사용하는 것이 최선으로 보였다.

그러나 글씨가 많거나, 표와 같은 컨텐츠는 사용자가 화면을 자유롭게 늘이거나 줄였을 때,

  1. 카드에 여백이 생기거나
  2. 카드 밖으로 표가 삐져나가는 상황 등

이 생겼다.
따라서,
카드 내부의 컨텐츠 높이를 인지한 후
컨텐츠 크기가 카드와 맞지 않으면 (작거나 크면)
해당 카드의 크기를 조절하는 작업이 필요했다.

처음에는 항상 그렇듯이 react의 useRef를 사용하여 요소의 크기를 파악하는 방법으로 구현했었다.

그런데,
동적으로 변하는 요소의 크기 정보를 감지하여, 레이아웃을 변경하는 함수를 호출하기 위해서는 useEffect를 이용하여 변화를 파악할 필요가 있었다.

이때, 문제가 발생했는데
react에서 ref 객체는 변화가 감지되지 않는다는 점이다.

나의 경우는 창 크기의 변화 / 요소 크기의 변화
이 두 가지 모든 경우에서 내부의 크기에 민감하게 반응하여 바로 바로 바뀔 수 있는 기능이 필요했다.

callback ref

아래는 callbackRef의 가장 기본적인 예시다.

재렌더링마다 callbackRef 내부로직이 실행되고, 이것이 height을 바꿔주기 때문에
새로운 height으로 컴포넌트를 재렌더링 시킬 수 있었다.

import React, { useState, useCallback } from "react";


export default function App() {
  const [height, setHeight] = useState(0);
  const callbackRef = (element) => {
    if (element) {
      setHeight(element.getBoundingClientRect().height);
    }
  };

  return (
    <div>
   	<input ref={callbackRef}>
    </div>
  );
}

따라서 height의 변화를 감지하여 layout을 변화시킬 수 있게 되었다.

export default function App() {
  
  const [height, setHeight] = useState<number>(0);

  const cardInnerRef = (element: HTMLDivElement) => {
    if (element) {
      const newHeight = element.getBoundingClientRect().height;
      setHeight(newHeight);
    }
  };

  const calNewHeight = () => {...};

  useLayoutEffect(() => {
    if (height && !isResponsive) {
      calNewHeight();
    }
  }, [layoutWidth, height]);
}

동적으로 변하는 것이 아니었다면,

  1. useRef
  2. useEffect (빈 dependency 배열)
  3. useState

위 3가지를 이용해서 mount 시점에의 element 크기를 구할 수 있었겠지만,
동적으로 변하는 것을 감지해야하는 부분을 고려해야했기 때문에 callBack Ref를 사용할 수 있었다.

0개의 댓글