스토어에는 원시타입, 객체, 함수를 저장할 수 있고 set 함수를 이용해 state를 merge 할 수 있다.
import { create } from 'zustand'
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newBears) => set({ bears: newBears }),
}))
zustand는 provider를 필요로 하지 않아서 간편하다.
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
zustand는 set 함수를 이용해서 새로운 state를 업데이트 한다.
import { create } from 'zustand'
type State = {
firstName: string
lastName: string
}
type Action = {
updateFirstName: (firstName: State['firstName']) => void
updateLastName: (lastName: State['lastName']) => void
}
const usePersonStore = create<State & Action>((set) => ({
firstName: '',
lastName: '',
updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
updateLastName: (lastName) => set(() => ({ lastName: lastName })),
}))
function App() {
const firstName = usePersonStore((state) => state.firstName)
const updateFirstName = usePersonStore((state) => state.updateFirstName)
return (
<main>
<label>
First name
<input
onChange={(e) => updateFirstName(e.currentTarget.value)}
value={firstName}
/>
</label>
<p>
Hello, <strong>{firstName}!</strong>
</p>
</main>
)
}
store를 설정할때 state랑 action함수의 타입을 같이 정의할 수 있다.
type State = {
deep: {
nested: {
obj: { count: number }
}
}
}
만약에 다음과 같이 깊은 상태 객체가 있다면 spread operator로 이용하는 가장 평범한 접근방법이 있다.
normalInc: () =>
set((state) => ({
deep: {
...state.deep,
nested: {
...state.deep.nested,
obj: {
...state.deep.nested.obj,
count: state.deep.nested.obj.count + 1
}
}
}
})),
하지만 위와같은 방법은 코드가 가독성이 떨어지므로 이에 대한 대안책을 소개하겠다.
immerInc: () =>
set(produce((state: State) => { ++state.deep.nested.obj.count })),
깊은 객체가 있을땐 immer를 사용해서 위와같이 코드를 작성하면 불변성을 유지하면서 가독성이 이전보다 뛰어난 코드를 작성할 수 있다.