거의 알고리즘 일기장

react context rerender 방지하기 (with: constate, use-context-selector) 본문

web

react context rerender 방지하기 (with: constate, use-context-selector)

건우권 2023. 3. 14. 02:03

react에서 prop을 다 넘겨주는 일은 상상이상으로 번거롭다.

귀찮아..

이때, react의 context를 사용하면, prop drilling 없이도 컴포넌트 트리 전체에서 데이터를 가져다 쓸수있다.

 

그런데 이제는 이런 문제?가 생긴다.

예시그림

자식1은 a데이터만 쓰고 자식2는 b데이터만 쓴다고 가정하자.

이때, a데이터가 변경되었는데, 자식1뿐만이아니라 자식2도 리랜더링 된다.

 

지금 우리의 프로덕트의 규모가 큰편이라 성능 최적화 관점에서 이 상황에서 자식 1만 리랜더링 되었으면 좋겠다.

 

어떻게 할까?


방법 1. context를 나눈다.

a와 b가 현재 하나의 context인데 이걸 두개로 나눈다. 

간단하쥬??

예시그림

그럼 context를 저렇게 손수 하나하나 만들어야 하나요??..

ㅡㅡ

이런 불편한 부분은 당연히 자동화 한 라이브러리가 있다.

바로 constate다.

 

해당 레포의 basic example을 보면, context를 여러개 만들어주는 함수가 있어 간단하게 나누어줄수 있다!

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

// 1️⃣ Create a custom hook as usual
function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(prevCount => prevCount + 1);
  return { count, increment };
}

// 2️⃣ Wrap your hook with the constate factory
const [CounterProvider, useCounterContext] = constate(useCounter);

function Button() {
  // 3️⃣ Use context instead of custom hook
  const { increment } = useCounterContext();
  return <button onClick={increment}>+</button>;
}

function Count() {
  // 4️⃣ Use context in other components
  const { count } = useCounterContext();
  return <span>{count}</span>;
}

function App() {
  // 5️⃣ Wrap your components with Provider
  return (
    <CounterProvider>
      <Count />
      <Button />
    </CounterProvider>
  );
}

https://github.com/diegohaz/constate

 

GitHub - diegohaz/constate: React Context + State

React Context + State. Contribute to diegohaz/constate development by creating an account on GitHub.

github.com


방법2: use-context-selector를 이용한다. 

이 모듈은 위에서 context를 나누는 것과 다른방법을 이용해서 rerender를 막는다.

readme의 Technical memo 섹션을 보면 useReducer의 cheat mode를 이용했다고 하는데.. 좀 봤는데, 뭔소리를 하는지 잘이해는 안간다. 혹시 궁금하다면, 아래의 링크를 참고하면 된다.

https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks

 

A Complete Guide to useEffect

Effects are a part of your data flow.

overreacted.io

 

 

아무튼 사용법을 설명하자면, 나는 이런형식으로 jetbrain의 live template에 추가해서 사용했다.

import { useState } from "react";
import { createContext, useContextSelector } from 'use-context-selector';

/**
 * @description 사용하는 컨텍스트의 로직을 작성하는 곳입니다.
 */
const useTest = () => {
  const [value, setValue] = useState(1);

  return {value, setValue};
};

/**
 * @description 컨텍스트를 생성합니다.
 * @description 여기서 type 을 명시해줍니다..
 */
const context = createContext<ReturnType<typeof useTest>>(
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  null,
);

/**
 * @description 컨텍스트를 제공하는 컴포넌트입니다.
 */
export const TestContextProvider = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const value = useTest();
  return <context.Provider value={value}>{children}</context.Provider>;
};

/**
 * @description 컨텍스트를 사용하는 hook 입니다.
 */
export const useTestContext = <K extends keyof ReturnType<typeof useTest>>(
  key: K,
) => {
  return useContextSelector(context, (value) => value[key]);
};

사용하는 곳에서는 이렇게 사용하면 된다.

//해당 provider로 감싼 컴포넌트 안
const value = useTestContext("value");
const setValue = useTestContext("setValue");

나는 두개 다 사용해봤는데, 개인적으로는 두번째 방법인 use-context-selector를 이용하는게 더 편했다.


 

근데 내 개인적인 생각으로는 대부분이 닥면하는 문제가 rendering이 제때 안되는게 문제지 rendering이 뭐 두번되고 세번되고는 이후 프로덕트가 커지고 앱이 무거워졌을때 고민하는게 좋다고 생각한다.

 

그러니 신경 안쓰고 개발하다가 "응? 좀 랜더링 속도가 거슬리는데?" 할때 이러한 기술들을 이용해보자.

반응형
Comments