오진수의 작업실
 · 프로그래밍

[Zustand] The result of getSnapshot should be cached to avoid an infinite loop 에러 해결하는 법

화면 캡처 2025-03-11 101643.png

Zustand를 사용하다 보면 한번쯤 마주하는 에러입니다. 내용을 보면 "The result of getSnapshot should be cached to avoid an infinte loop"라고 하는데요. 아래와 같이 Maximum update depth exceeded 에러와 함께 뜹니다.

화면 캡처 2025-03-11 101632.png

이 에러를 발생시키는 대표적인 예시 코드는 아래와 같습니다.


function Component() {
  const values = useStore((state) => [1, 2, 3])
  
  return <p>{values.join(", ")}</p>
}


언뜻 별 문제 없어 보이는 단순한 코드지만 에러가 발생합니다. 그 원인은 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는 기존에 React에서 useMemouseCallback이 해주는 역할과 유사한 역할을 수행합니다.

프로필 이미지

댓글

프로그래밍 카테고리 다른 글

  1. Flutter 상태관리 Provider면 충분하다Flutter 상태관리 Provider면 충분하다
     · 프로그래밍 · 댓글 1