useContext 최적화

김동하·2023년 3월 25일
1

react

목록 보기
31/31

들어가며

useContext를 안 쓰다가 컴파운드로 컴포넌트를 만들면서 종종 사용하기 시작했다. context api는 친숙하지 않고 전체 렌더링 이슈 있어서 뭔가 찝찝했는데 이번 기회에 context를 최적화 해보장!

예제

위와 같은 앱이 있다고 가정하자. 주요 컴포넌트는 두 개다.

  • dog name을 value로 받는 input

    • value가 있으면 화면에 value를 렌더링한다.
  • 사용자가 지정한 만큼 cell을 보여주는 grid

    • update 버튼으로 cell을 새로 렌더링한다.
    • checkbox를 통해 grid를 계속 새롭게 렌더링한다.
    • row, col을 수정하여 UI를 업데이트 한다.
    • cell을 클릭하면 랜던함 숫자로 cell을 업데이트 한다.

두 컴포넌트는 Provider의 children이고

Provider는 state와 dispatch를 value로 준다.

가장 큰 렌더링 비용을 사용하는 Grid는 React.memo를 한다.

이제 강제 리렌더링을 해보자!

보는 것처럼 컴포넌트에 React.memo를 해줬음에도 모든 컴포넌트가 리렌더링 됐다.

그 이유는 provider에 props로 주는 value가 참조객체이기 때문에 컴포넌트는 매번 새로운 props로 인지하는 것이다.

위 value를 useMemo로 메모해준다.

그리고 다시 테스트 해보면

value의 변화가 없을 때, 모든 컴포넌트가 리렌더링이 되지 않는 것을 확인할 수 있다!

further

이제 Grid 컴포넌트에서 state 변화가 일어났다고 가정하자.

가령, cell을 클릭하여 cell에 적힌 숫자를 변경한다고 하자.

위에 보이는 것처럼 업데이트가 된 Cell은 하나인데 전체가 리렌더링 된 것을 볼 수 있다.

memo를 했음에도 이러한 현상이 일어난 이유는 생각해보면 간단하다.

useMemo의 의존성 배열에 있는 state 즉, dogName, grid 중 grid가 변경되었으니 새로운 value 를 만든 것이다.

<Cell> 컴포넌트의 부모인 <Grid> 컴포넌트는 실질적으로 state 변경과 상관이 없음에도 리렌더링이 된다.

실제 UI 업데이트가 일어나는 하나의 Cell만 리렌더링을 시켜야 한다!

가장 쉬운 방법은 context를 분리하는 것이다.

dispatch context를 따로 만들고

state, dispatch 를 필요한 곳에만 준다!

그리고 이전과 동일하게 cell을 클릭해서 리렌더링을 발생시키면

모든 컴포넌트가 렌더링 되지 않음을 알 수 있다!

context 분리 전

context 분리 후

further

아직 이상한 부분이 있다. 분명 하나의 cell을 클릭했는데 상관없는 몇몇 cell들이 리렌더링 되는 것이다.

아래 보이는 것처럼 memo는 되고 있지만 리렌더링 되는 cell들이 있다. (처음엔 버그인 줄 알았다.)

그 이유는 <Cell> 컴포먼트가 state를 다루는 데 있는데, <Cell> 컴포먼트는 <Cell> 는 직접 state를 받고 있는 일과 받은 state를 통해 cell을 렌더링 하는 일. 두 가지 일을 하고 있기 때문이다. (이 부분의 이해가 확실하지 않음)

이를 해결 하기 위해서는 로직을 분리하여 새로운 컴포너틑를 만들어주는 것이다. 그리고 props를 통해 전달하여 React.memo가 제대로 작동할 수 있게 해주는 것이다.

먼저 <Cell> 컴포넌트에서 provider에서 받은 state와 관련된 로직을 지우고 <CellImpl>로 컴포넌트 이름을 변경한다.

아래와 같이 <Cell> 컴포넌트를 만들고 state를 받고 필요한 로직을 거쳐 cell을 <CellImpl>에게 props로 준다.

그리고 다시 테스트를 해보면

<CellImpl> 는 렌더링 되지 않았고

<Cell> 는 렌더링되었다.

즉, react element를 반환하는 <CellImpl>의 불필요한 렌더링을 막으면서 최적화한 것이다. (이거 짱이다...)

참고

profile
프론트엔드 개발

0개의 댓글