공식 홈페이지의 얕은 함정
Zustand 공식 홈페이지에 들어가면 가장 먼저 보이는 예제입니다.
import { create } from 'zustand'
const useStore = create((set) => ({
count: 1,
inc: () => set((s) => ({ count: s.count + 1 })),
}))
function Counter() {
const { count, inc } = useStore()
return (
<div>
<span>{count}</span>
<button onClick={inc}>one up</button>
</div>
)
}
물론 이 코드를 그대로 복사해도 문제없이 잘 돌아가는듯 합니다.
그러나 프로젝트가 커지면서 '원인 모를 리렌더'가 빈번히 발생하고,
추적끝에 Zustand를 사용하면서 발생한 문제라는 것을 알게됩니다.
셀렉터 개념을 알아야 해요!
다음 예시코드를 통해 테스트 해 봅시다.
const useAnimalStore = create<AnimalState>((set) => ({
bear: 0,
shark: 0,
addBear: () => set((state) => ({ bear: state.bear + 1 })),
addShark: () => set((state) => ({ shark: state.shark + 1 })),
}));
function WholeBox() {
const { bear } = useAnimalStore();
console.log(`WholeBox render & bear:${bear}`);
return <></>;
}
function SelectorBox() {
const bear = useAnimalStore((state) => state.bear);
console.log(`SelectorBox render & bear:${bear}`);
return <></>;
}
export default function Page() {
const addBear = useAnimalStore((state) => state.addBear);
const addShark = useAnimalStore((state) => state.addShark);
return (
<>
<button onClick={addBear}>addBear</button>
<button onClick={addShark}>addShark</button>
<SelectorBox />
<WholeBox />
</>
);
}
Add Bear 버튼과 Add Shark버튼 UI가 존재하며, 클릭 시 각각에 해당하는 state(bear, shark)를 증가시킵니다.
여기서 집중해서 볼것은 bear를 사용하고 있는 WholeBox와 SelectorBox의 리렌더링에 따른 console.log 출력 여부입니다.
| 컴포넌트 | bear 사용을 위해 작성한 Store 호출 방식 | 콘솔 출력(리렌더)을 트리거하는 버튼 |
| WholeBox | const { bear } = useAnimalStore(); | Add Bear, Add Shark |
| SelectBox | const bear = useAnimalStore((state) => state.bear); | Add Bear |
두 컴포넌트 모두 스토어에서 bear만 사용하는 것 같은데 shark의 상태를 변경해도 WholeBox에서는 불필요하게 리렌더링이 발생합니다.
차이점을 발생시킨 것은 두 컴포넌트의 useAnimalStore 호출 방식입니다.
1. WholeBox(셀렉터 문법 미적용)
const { bear } = useAnimalStore();
- 공식홈페이지를 들어가자 마자 보았던 예제
- 스토어 전체 객체를 반환(구독)
- 상태가 하나라도 바뀌면 새로운 스토어 객체가 생성됨
- 스토어 전체를 구독하는 컴포넌트는 모두 리렌더됨
2. SelectorBox(셀렉터 문법 적용)
const bear = useAnimalStore((state) => state.bear);
- 공식문서 중 Render Optimization에서 설명하고 있는 예제
- 셀렉터가 해당 state만 골라서 반환(구독)
- 다른 상태가 바뀌더라도 해당 state만 구독하는 컴포넌트는 리렌더되지 않음
'React' 카테고리의 다른 글
| React 컴포넌트가 순수해야 하는 이유 (0) | 2025.07.04 |
|---|---|
| React Core 구현하기 - 2. JSX 런타임 로직 (0) | 2025.02.18 |
| React Core 구현하기 - 1. JSX 컴파일 (0) | 2025.02.14 |
| React18 useEffectEvent (0) | 2024.12.30 |
| React useLayoutEffect (2) | 2024.12.27 |