[Zustand] The result of getSnapshot should be cached to avoid an infinite loop 에러 해결하는 법
내용을 보면 이러한데 아래와 같이 Maximum update depth exceeded 에러와 함께 뜹니다.
아래와 같은 단순한 코드가 에러를 발생시키는데요.
const values = useStore((state) => [1, 2, 3])
에러가 발생하는 원인은 useStore 함수가 작동하는 방식에 있습니다.
useStore는 파라미터로 받은 함수를 이용해 values
처럼 결과로 뽑아냅니다. 그리고 values
는 변경될 때마다 컴포넌트를 다시 렌더링합니다. 이때 컴포넌트를 다시 렌더링하면서 useStore는 다시 파라미터로 받은 함수를 이용해 새로운 결과를 뽑아냅니다. 만약 새로 뽑아낸 결과가 기존 결과랑 다르다면 리턴값 values
를 새로 업데이트합니다. 그러면 컴포넌트가 또 다시 렌더링되겠죠. 이렇게 무한 반복입니다.
반복을 피하는 방법은 새로운 결과를 기존 결과와 일치시키는 겁니다. 언뜻 보기에 [1, 2, 3]
배열은 기존 결과와 똑같을 거라고 예상할 수 있지만, 실제로는 다릅니다. 배열은 원시 값이 아니기 때문에 비교했을 때 서로를 같은 객체가 아니라고 판별합니다. 아래 코드가 무한 반복을 일으키지 않는 것과는 다르게 말입니다.
const value = useStore((state) => 1);
여기서 1은 원시값이기 때문에 그렇습니다. 쉽게 말해서 1 === 1
이지만, [] !== []
인 것이죠.
하지만 매번 원시값을 사용할 수는 없기 때문에 배열 같은 참조 객체를 결과로 사용하는 방법은 바로 Zustand가 지원하는 useShallow
함수를 사용하는 것입니다.
import useShallow from "zustand/shallow";
const values = useStore(useShallow((state) => [1, 2, 3]));
문법이 길어졌지만 어쩔 수 없네요. 아무튼 useShallow
는 useMemo
나 useCallback
이 해주는 역할을 동일하게 해줍니다.