본문 바로가기

FrontEnd

리액트 성능 최적화 :contextAPI

반응형

컨텍스트 API는 props drilling을 막기 위한 수단일 뿐이며,

내부의 state가 갱신되면, 하위 컴포넌트들이 전부 렌더링되는, 리액트 컴포넌트의 룰을 벗어나지 않는다.

즉, <CountProvider />가 다시 렌더링될 때마다 하위 컴포넌트는 전부 렌더링된다.

const CountContext = React.createContext()

function CountProvider(props) {
  const [count, setCount] = React.useState(0)
  const value = [count, setCount]
  return <CountContext.Provider value={value} {...props} />
}

언제 zustand/redux를 사용하고 언제 context API를 사용할까?

 

[짤막글] 언제 context API를 사용하고 언제 zustand나 redux를 사용할까요?

TLDR : (!트리_아래_업데이트 && context) || zustand 긴말 필요없이 아래 트윗을 인용합니다. (Tanner Linsley === 리액트 쿼리 메인테이너, 창시자) https://twitter.com/tannerlinsley/status/129364099953356..

itchallenger.tistory.com

해결방법 1: contextProvider내에서 state 메모하기.

난 진정한 해결방법이라 생각하지 않는다.

상단 컴포넌트의 useMemo는 하위 컴포넌트가 memo를 사용했을 때만 효과가 있다.

useCallback도 마찬가지다.

이를 useMemo의 전파라 부를 수 있겠다.

 

const CountContext = React.createContext()

function CountProvider(props) {
  const [count, setCount] = React.useState(0)
  const value = React.useMemo(() => [count, setCount], [count])
  return <CountContext.Provider value={value} {...props} />
}
function AppProvider({children}) {
  const [state, dispatch] = React.useReducer(appReducer, {
    dogName: '',
    grid: initialGrid,
  })
  const value = React.useMemo(() => [state, dispatch], [state])
  return (
    <AppStateContext.Provider value={value}>
      {children}
    </AppStateContext.Provider>
  )
}

useMemo 사용 이유는 1. 스테이블한 값 제공. 2. 비싼 연산 결과 저장

스파이크의 유무를 볼 수 있음.

또한 위 방법은, state가 변경되면 아무 의미가 없다.

해결방법 2 : Dispatch providerDispatch consumer 분리

dispatch만 원하는 컴포넌트에서는 dispatch만 사용하여, state 변경시 리렌더링 영향을 막자.

(당연히 메모는 해줘야 한다.)

중간의 Grid 컴포넌트는 디바운스 설정, 디스패치 함수만 넘겨주는 컴포넌트이기 때문에, 리렌더링 할 필요가 없다.

useAppDispatch를 사용하는 컴포넌트들은, state가 불변이므로 컨텍스트 값에 영향받지 않는다.

(물론 상단 컴포넌트 리렌더링의 영향은 받기 때문에 메모해줘야 한다.)

useAppState를 사용하는 컴포넌트들은 상태 변경 시 리렌더링된다.

function AppProvider({children}) {
  const [state, dispatch] = React.useReducer(appReducer, {
    dogName: '',
    grid: initialGrid,
  })
  return (
    <AppStateContext.Provider value={state}>
      <AppDispatchContext.Provider value={dispatch}>
        {children}
      </AppDispatchContext.Provider>
    </AppStateContext.Provider>
  )
}

function useAppState() {
  const context = React.useContext(AppStateContext)
  if (!context) {
    throw new Error('useAppState must be used within the AppProvider')
  }
  return context
}

function useAppDispatch() {
  const context = React.useContext(AppDispatchContext)
  if (!context) {
    throw new Error('useAppDispatch must be used within the AppProvider')
  }
  return context
}

여기서 좀 더 최적화하는 방법은 https://itchallenger.tistory.com/552 참고

반응형