이전의 React-redux에서 Context API의 렌더링이슈를 해결한 방법
react-redux는 구독/발행 패턴으로 구현되었다.
React와 사용하기 위해 ContextAPI를 사용한다.
Context API는 렌더링마다 새 객체를 생성하게 되어 하위 컴포넌트들이 모두 리렌더링 되는 이슈가 있다.
React-redux에서는 shallowEqual이라는 내장함수를 제공하는데, 이를 통해 Context API의 렌더링 이슈를 해결할 수 있다.
동작 방식
react-redux에서는 useSelector의 equalityFn은 이전값과 다음값을 비교하여 true일경우 리렌더링을 하지 않고 false가 나오면 리렌더링을 한다.
리렌더링 방식은 다음과 같다.
useSelectorWithStoreAndSubscription
Hook에는 강제로 렌더링을 트리거하는 forceRender함수가 존재한다.
그리고 store또는 subscription이 변경될 경우 실행되는 useEffect Hook이 존재한다.
이 Hook에서는 parameter로 전달받은 equalityFn
을 사용하여 이전값과 업데이트될 값을 비교한다.
1 | type equalityFn = (left: any, right: any) => booleant; |
즉 equalityFn에서 true를 반환할 경우 아무 작업도 하지 않고 useEffect를 탈출한다. 값이 다르다면 state를 업데이트 한 후 forceRender 함수를 실행한다.
여기서 state는 useRef를 통해서 관리되고 있기 떄문에 forceRender 함수를 실행하여 강제로 렌더링을 하지 않는다면 단순 state의 변화로 렌더링이 일어나지 않는다.
Object.is
Object는 형변환이 일어나지 않는 얕은비교를 한다.
즉 형변환 없이 같은 값인지를 반환한다.
예를들어 -0과 +0또한 다른값으로 한다는 이야기이다.
mdn의 폴리필은 다음과 같다.
1 | if (!Object.is) { |
ShallowEqual
ShallowEqual은 객체의 depth가 1인 값들을 모두 비교한다.
1 | function is(x, y) { |
ShallowEqual을 사용하여 객체의 reference의 변경만으로 렌더링을 트리거하지 않도록 할 수 있게 되었다.
reat-redux는 현재 React 18부터 제공되는 useSyncExternalStoreHook을 사용하도록 변경되었다.
하지만 구독/발행 패턴에서 기존의 ContextAPI의 렌더링이슈를 해결한 방법에 대해서 다룰 가치가 있다고 생각하여 다루어보았다.
위에서 언급한 useSelectorWithStoreAndSubscription
의 전체 코드는 다음과 같다.
1 | function useSelectorWithStoreAndSubscription( |
useSyncExternalStore
가 개선한 사항들은 여기랑 여기서 볼 수 있다.
다음에는 zustand 코드를 보고 ContextAPI를 사용하지 않고 상태관리를 한 방법에 대해서 알아보려고 한다.