[React] memo가 잘 적용되지 않는다면? (react-router)

coderH·2022년 12월 7일
0

얼마 전 리액트 프로젝트의 성능을 위해 memo를 적용하여 컴포넌트의 재사용성을 높이는 작업을 진행하고 있었습니다.

이 과정에서 memo를 적용하였음에도 일부 컴포넌트에서 계속 리렌더링이 발생했고 영향을 줄 수 있는 요소들을 꼼꼼히 확인하였음에도 원인을 찾지 못하고 있었습니다.

하지만, 해당 컴포넌트들의 공통점을 찾아보니 컴포넌트 내부에서 react-router의 훅 중 하나인 useNavigate를 사용하고 있었고 일시적으로 navigate를 사용하지 않도록 처리하자 정상적으로 작동하는걸 확인했습니다.

이에 해당 훅과 관련된 내용을 구글링 해보았고 저와 비슷한 현상을 겪은 사람의 글을 찾을 수 있었습니다.

원인은 react-router가 V6로 업그레이드 되면서 URL이 변경 될 경우 useNavigate함수를 계속 재생성하도록 변경 되었다는것을 알게 되었습니다.

사실, 이 부분은 일반적인 상황에서는 크게 문제가 될게 없지만 제 프로젝트의 경우 클릭할 때마다 아이템의 id값이 URL의 query에 붙는 형태이기 때문에 이로 인한 불필요한 리렌더링이 계속 발생하는것이였습니다.

해결방법

이를 해결하기 위해서는 값이 변경 되더라도 리렌더링을 유발하지 않는 ref 객체를 활용해 해결할 수 있습니다.

즉, navigate함수를 useRef의 값으로 넣고 각 컴포넌트에서는 Context API를 통해 navigate함수에 접근하는것입니다.

// navigate 함수를 제공해줄 Context
const StableNavigateContext = createContext(null);

// navigate 함수를 ref의 값으로 집어넣어 Context API를 활용해 제공
const StableNavigateContextProvider = ({ children }) => {
  const navigate = useNavigate();
  const navigateRef = useRef(navigate);

  return (
    <StableNavigateContext.Provider value={navigateRef}>
      {children}
    </StableNavigateContext.Provider>
  );
};

// 컴포넌트 내부에서 사용할 훅
const useStableNavigate = () => {
  const navigateRef = useContext(StableNavigateContext);
  if (navigateRef?.current === null) {
    throw new Error("StableNavigate context is not initialized");
  }

  return navigateRef.current;
};

이렇게 하면 URL변경으로 인해 useNavigate훅이 재생성 되더라도 리렌더링이 발생하지 않으므로 의도했던대로 동작할 수 있게 됩니다.

따라서, 이러한 로직을 별도의 StableNavigate라는 커스텀 훅으로 분리하여 해결할 수 있습니다.

일반 Navigate와 StableNavigate를 사용 할 때의 차이는 아래의 제 코드샌드박스로 예시를 만들어 두었으니 참고하시길 바랍니다.

navigation_re-render_test | Codesandbox

참조

0개의 댓글