Updating State
June 4, 2024
Zustand
Zustand 공식문서를 참고하여 번역함.
# Flat Updates
Zustand를 사용하여 상태를 업데이트하는 것은 간단합니다! 제공된 set
함수를 새로운 상태와 함께 호출하면, 그 상태는 스토어의 기존 상태와 얕게 병합됩니다. 참고: 중첩된 상태에 대한 내용은 다음 섹션을 참조하세요.
import { create } from 'zustand'type State = {firstName: stringlastName: string}type Action = {updateFirstName: (firstName: State['firstName']) => voidupdateLastName: (lastName: State['lastName']) => void}// Create your store, which includes both state and (optionally) actionsconst usePersonStore = create<State & Action>((set) => ({firstName: '',lastName: '',updateFirstName: (firstName) => set(() => ({ firstName: firstName })),updateLastName: (lastName) => set(() => ({ lastName: lastName })),}))// In consuming appfunction App() {// "select" the needed state and actions, in this case, the firstName value// and the action updateFirstNameconst firstName = usePersonStore((state) => state.firstName)const updateFirstName = usePersonStore((state) => state.updateFirstName)return (<main><label>First name<input// Update the "firstName" stateonChange={(e) => updateFirstName(e.currentTarget.value)}value={firstName}/></label><p>Hello, <strong>{firstName}!</strong></p></main>)}
# Deeply nested object
깊은 상태 객체가 아래와 같다면:
type State = {deep: {nested: {obj: { count: number }}}}
중첩된 상태를 업데이트하려면 불변성을 유지하면서 작업을 완료하기 위해 약간의 노력이 필요합니다.
일반적인 접근
React나 Redux와 마찬가지로, 일반적인 접근 방식은 상태 객체의 각 레벨을 복사하는 것입니다. 이는 스프레드 연산자 ...
를 사용하여 수행되며, 새로운 상태 값을 수동으로 병합하여 이루어집니다. 다음과 같이 합니다:
normalInc: () =>set((state) => ({deep: {...state.deep,nested: {...state.deep.nested,obj: {...state.deep.nested.obj,count: state.deep.nested.obj.count + 1}}}})),
이 방법은 매우 번거로울 수 있습니다! 이제 삶을 더 편하게 만들어줄 몇 가지 대안을 살펴보겠습니다.
With Immer
많은 사람들이 중첩된 값을 업데이트하기 위해 Immer
를 사용합니다. Immer는 React, Redux, 그리고 물론 Zustand와 같이 중첩된 상태를 업데이트해야 할 때 언제든지 사용할 수 있습니다!
깊게 중첩된 객체의 상태 업데이트를 간소화하기 위해 Immer를 사용할 수 있습니다. 예제를 살펴보겠습니다:
immerInc: () =>set(produce((state: State) => { ++state.deep.nested.obj.count })),
코드가 많이 줄었습니다! 여기 나열된 주의사항
들을 꼭 확인하세요
With optics-ts
다른 옵션으로는 optics-ts가 있습니다:
opticsInc: () =>set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),
Immer와 달리, optics-ts는 프록시나 mutation 문법을 사용하지 않습니다.
With Ramda
또한 Ramda를 사용할 수 있습니다:
ramdaInc: () =>set(R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)),
ramda와 optics-ts 모두 타입과 함께 작동합니다.
CodeSandbox Demo
https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx