reactive variable (makeVar)의 문제점

설명
Context API 와 동일한 이슈가 있다.
Tags
state

Apollo Client에서 local state 관리

 
단순히 makrVar()로만 선언한 reactive variable의 값의 변화는 컴포넌트를 리렌더링 시키지 않는다.
 
useReactiveVar()로 감싸야 reactive variable의 값을 컴포넌트의 state로 다이렉트로 사용하게 되며, 이 경우에는 reactive variable의 값이 바뀐다면 컴포넌트의 state가 바뀐 것이므로 컴포넌트도 리렌더링 된다.
 

apollo global state의 문제점

리액트 상태관리 라이브러리 역사와 Recoil
에서 Context API의 단점을 설명하는 부분을 참고하자.
 
apollo의 global state는 Context API의 단점과 완전히 동일한 이슈가 있다.
상태의 일부만 바뀌었더라도 전체가 바뀌었다고 인지하여 해당 상태를 구독하고 있는 모든 컴포넌트가 리렌더링 된다.
( 비록 해당 컴포넌트가 참조하는 값은 이전과 동일함에도 불구하고! )
 
// ---- global state 선언 ------------------------------- const apolloState = makeVar({ id: 0, name: 'n' }); // ---- getter ----------------------------------------- // makeVar의 getter()를 직접 참조하는 경우에는 값이 바뀌더라도 컴포넌트를 리렌더링을 일으키지 않음. const useGetId = () => { const {id} = apolloState(); return id; } // useReactiveVar로 감쌌으므로 값이 바뀌면 컴포넌트도 리렌더링 된다. // id 값만 반환하므로 name이 바뀔 때는 리렌더링이 되지 않았으면 하지만, 실제로는 name이 바뀌어도 리렌더링 된다. const useGetReactiveId = () => { const {id} = useReactiveVar(apolloState); return id; }
 
영상의 빨간 부분은 id 값만 변경시키고, 초록 부분은 name 값만 변경시키는 예시다.
{ id: number, name: string } 형태의 상태를 선언하였다.
 
Profiler로 보면 id 값만 바꿀 때도 name 값만 사용하는 컴포넌트도 같이 리렌더링되고, 반대의 경우도 마찬가지임을 알 수 있다.
 
즉, Context API와 마찬가지의 해결책으로 두 개의 값의 변화에 독립적으로 관리하고 싶다면, reactiveVar를 각각 별도로 선언하여 사용하여야 한다.
 

해결방안

  1. 각각 독립적으로 makeVar()를 선언하여 사용한다.
  1. redux에서는 shallowEqual이라는 옵션을 주어서 해결할 수 있었다.
      • apollo client에는 shallowEqual과 같은 옵션이 없다.
  1. atom 기반의 상태관리 라이브러리를 쓰자 ← 추천!
      • recoil ← 추천!
      • jotai