01. 설치하기
npm install zustand
02. 사용법
import { create } from 'zustand'
const useStore = create((set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
}))
function Counter() {
const { count, inc } = useStore()
return (
<div>
<span>{count}</span>
<button onClick={inc}>one up</button>
</div>
)
}
스토어는 훅입니다. 여기에는 객체, 함수 등 무엇이든 넣을 수 있습니다. 상태는 불변적으로 업데이트되어야 하며, 설정 함수는 이를 돕기 위해 상태를 병합합니다.
03. 유의할 점
Fetching everything vs. Selecting multiple state slices
Fetching everything
const state = useBearStore()
상태가 변경될 때마다 컴포넌트가 업데이트됩니다.
Selecting multiple state slices
const nuts = useBearStore((state) => state.nuts)
const honey = useBearStore((state) => state.honey)
기본적으로 엄격한 동일성(이전상태 === 새로운 상태)으로 변경 사항을 감지해서 state.nuts의 실행 결과를 기억하고 이전 결과가 다르지 않다면 리렌더링이 되지 않아서 불필요한 렌더링을 방지할 수 있습니다.
const { nuts, honey } = useBearStore(
useShallow((state) => {{
nuts: state.nuts,
honey: state.honey,
}))
);
zustand에서 useStore를 사용해서 여러 개의 값을 꺼낼 때에는 한번에 각각 사용해서 따로 꺼내는 방법이 있고 한 번에 객체 또는 배열 형태로 꺼내려면 useShallow를 사용해야 합니다.
Async action을 기본으로 지원
준비가 되면 set값을 호출하기만 하면 zustand는 작업의 비동기화 여부에 상관하지 않습니다.
const useFishStore = create((set) => ({
fishies: {},
fetch: async (pond) => {
const response = await fetch(pond)
set({ fishies: await response.json() })
},
}))
Immer.js의 필요성
set 함수는 상태를 한 개의 깊이만 병합합니다. 중첩된 개체가 있는 경우 명시적으로 병합해야 합니다. 다음과 같이 스프레드 연산자 패턴을 사용합니다.
import { create } from 'zustand'
const useCountStore = create((set) => ({
nested: { count: 0 },
inc: () =>
set((state) => ({
nested: { ...state.nested, count: state.nested.count + 1 },
})),
}))
중첩된 구조를 줄이는 것은 번거로운 작업으로 immer를 사용하면 이 문제를 해결할 수 있습니다.
// immer의 기본 사용법
import { produce } from 'immer'
const useLushStore = create((set) => ({
lush: { forest: { contains: { a: 'bear' } } },
clearForest: () =>
set(
produce((state) => {
state.lush.forest.contains = null
}),
),
}))
const clearForest = useLushStore((state) => state.clearForest)
clearForest()
produce 함수의 첫 번째 매개변수로 전달된 함수는 수정할 로직을 포함하는 함수이며, 이 함수의 인자 state는 원본 상태입니다. 해당 함수 내에서 state를 변경하는 것은 수정할 로직을 작성하는 부분을 레시피라고 부릅니다.
Immer middleware
Immer 미들웨어를 사용하면 보다 편리한 방식으로 불변 상태를 사용할 수 있습니다. 또한 Immer를 사용하면 Zustand에서 변경 불가능한 데이터 구조를 간편하게 처리할 수 있습니다.
사용 예시
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
type State = {
count: number
}
type Actions = {
increment: (qty: number) => void
decrement: (qty: number) => void
}
export const useCountStore = create<State & Actions>()(
immer((set) => ({
count: 0,
increment: (qty: number) =>
set((state) => {
state.count += qty
}),
decrement: (qty: number) =>
set((state) => {
state.count -= qty
}),
})),
)
Persist middleware
browser storage에 데이터 보관하기
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
const useFishStore = create(
persist(
(set, get) => ({
fishes: 0,
addAFish: () => set({ fishes: get().fishes + 1 }),
}),
{
name: 'food-storage', // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
},
),
)
모든 종류의 스토리지를 사용하여 스토어의 데이터를 유지할 수 있습니다.
'TIL' 카테고리의 다른 글
TIL - useMemo와 useCallback (0) | 2024.05.31 |
---|---|
TIL: React, Vite, Redux(RTK), Router를 이용한 SPA 블로그 프로젝트 (0) | 2024.05.30 |
TIL - 각 컴포넌트 폴더에 index.js 파일을 두는 이유 (0) | 2024.05.28 |
TIL - 부모 - 자식 컴포넌트의 리렌더링 (0) | 2024.05.27 |
TIL - Form Input - useRef vs useState (0) | 2024.05.26 |