스토어 생성하기
- 스토어는 훅이다
- 상태로 원시타입, 객체, 함수(action) 모두 들어갈 수 있음
- create 함수로 스토어 생성
- 안에 상태를 생성하는 함수가 들어간다
- 생성하는 함수의 매개변수로 set이 들어가며, 이 것은 상태 내에서 setState 역할을 한다
- set 다음에 get도 인자로 들어갈 수 있음 ⇒ 현재 상태를 지칭함
import { create } from 'zustand' const useBearStore = create((set) => ({ bears: 0, increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), removeAllBears: () => set({ bears: 0 }), }))
const useBearStore = create<BearState>((set, get) => ({ bears: 0, increasePopulation: () => set({ bears: get().bears + 1 }), }))
컴포넌트와 스토어를 연결하기
- 상태를 사용하고 싶은 컴포넌트에서 스토어 훅을 사용하여 상태들을 가져온다
- 스토어 훅 안에 원하는 상태를 꺼내는 함수를 넣는다
((state) => state.가져오고싶은상태)
function BearCounter() { const bears = useBearStore((state) => state.bears) return <h1>{bears} around here ...</h1> } function Controls() { const increasePopulation = useBearStore((state) => state.increasePopulation) return <button onClick={increasePopulation}>one up</button> }
상태 가져오기
- 모든 상태 가져오기
const state = useBearStore()
⇒ 모든 상태가 변할 때마다 컴포넌트가 업데이트 됨
⇒
state.상태
로 원하는 상태를 가져오거나 구조분해를 사용할 수 있음const { bears } = useBearStore()
- 원하는 상태만 가져오기
const bears = useBearStore((state) => state.bears)
⇒ strict-equality(일치 비교)를 통해 상태의 변화를 판단
동등성 비교
1. useShallow
- 객체, 배열 등의 변화를 얕은 비교로 감지한다
- 즉, 계산된 값(예시에선 상태의 키들)만 비교한다
const names = useMeals(useShallow((state) => Object.keys(state)))
2. 커스텀 함수
훅을 사용할 때, 인자의 두번째 값으로 커스텀 비교함수를 넣어 동등성을 비교할 수 있다
const treats = useBearStore( (state) => state.treats, (oldTreats, newTreats) => compare(oldTreats, newTreats), )
상태 값을 덮어쓰기
create 에서 쓰는 set의 두번째 인자로 true를 주면, 첫번째 인자인 상태 값으로 상태가 덮어써진다
cf) 기본값은 false, 기존 상태에서 첫번째 인자에 명시된 상태값만 변경된다
const useFishStore = create((set) => ({ salmon: 1, tuna: 2, deleteEverything: () => set({}, true), // clears the entire store, actions included }))
non-reactive 상태
- getState를 사용해서 비반응형 방식으로 상태에 접근할 수 있다
스토어.getState().상태
: non-reactive하게 상태를 가져옴
const useDogStore = create(() => ({ paw: true, snout: true, fur: true })) const paw = useDogStore.getState().paw //비반응형 방식으로 상태 얻어오기 const unsub1 = useDogStore.subscribe(console.log) //리스너 등록 useDogStore.setState({ paw: false }) //상태 업데이트 => 리스너 동작됨 unsub1() // Unsubscribe listeners // cf) 기존 방식 function Component() { const paw = useDogStore((state) => state.paw) ...
특정 상태가 변경될 때만 리스너 동작하게 하기
- create로 스토어를 생성할 때 내부 함수를
subscribeWithSelector
로 감싸줌
import { subscribeWithSelector } from 'zustand/middleware' const useDogStore = create( subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })), )
- 특정 상태를 지정 ⇒ selector: 특정 상태 꺼내는 함수
subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe
// paw가 변경됐을 때 const unsub2 = useDogStore.subscribe((state) => state.paw, console.log) // 두번째 인자 함수에서 바뀐 상태와 이전 상태를 받을 수 있음 const unsub3 = useDogStore.subscribe( (state) => state.paw, (paw, previousPaw) => console.log(paw, previousPaw), ) // 세번째 인자로 동등성 함수를 받을 수 있음 const unsub4 = useDogStore.subscribe( (state) => [state.paw, state.fur], console.log, { equalityFn: shallow }, ) // 옵션으로 즉시 실행 할 수 있게 지정할 수 있음 const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, { fireImmediately: true, })
ref의 값으로 상태를 받기
- 렌더링에 영향받지 않는 상수이지만, 상태가 업데이트될 때마다 값을 업데이트 하고 싶을 땐
- subscribe : 상태가 변화하면 실행되는 함수
⇒ useRef + subscribe !!
const useScratchStore = create((set) => ({ scratches: 0, ... })) const Component = () => { const scratchRef = useRef(useScratchStore.getState().scratches) useEffect(() => useScratchStore.subscribe( state => (scratchRef.current = state.scratches) ), []) ...