Skip to main content

Zustand Middleware: With Setter

@toktokhan-dev/zustand-with-setter 패키지는 Zustand 스토어에 setreset 함수를 추가하여 상태를 업데이트하고 초기화하는 기능을 제공합니다.

Installation

only zustand-with-setter

npm i @toktokhan-dev/zustand-with-setter

or with zustand-create-store-context

npm i @toktokhan-dev/zustand-react

Comparison

Vanilla Zusstand

기존 Zustand 는 상태와 더불어 업데이트 함수를 직접 정의하여 사용합니다. 간단한 상태 변경 또한 직접 정의해야하는 불편함이 있습니다.

import create from 'zustand'

type Store = {
count: number
nested: { count: number }
}

const useStore = create<Store>((set) => ({
count: 0,
nested: { count: 0 },
setCount: (count: number) => set(() => ({ count })),
setNestedCount: (count: number) => set((state) => ({ nested: { ...state.nested, count } })),
}))

const setCount = useStore((store) => store.setCount)

setCount(5)

Zustand With Setter

@toktokhan-dev/zustand-with-setter 패키지를 사용하면 store 에 setreset 함수를 추가하여, 간단한 상태변경은 함수를 따로 정의 하지 않아도 됩니다.

pnpm add zustand

import { withSetter } from '@toktokhan-dev/zustand-with-setter'
import { create } from 'zustand'

type Store = {
count: number
nested: { count: number; list?: string[] }
}

const useStore = create(
withSetter<Store>(() => ({
count: 0,
nested: { count: 0 },
})),
)

const set = useStore((store) => store.set)

set({ count: 5 })
set('count', 5)
set('nested.count', (prev) => prev + 1)

Usage

withSetter

zustand middleware 는 일반적으로 고차함수로써 store 생성 함수를 받아서 새로운 store 생성 함수를 반환합니다. withSetter 또한 같습니다. store 생성함수를 받아 setreset 함수를 추가한 새로운 store 생성 함수를 반환합니다.

import { withSetter } from '@toktokhan-dev/zustand-with-setter'

type Store = {
count: number
}

const useStore = create(
withSetter<Store>((set, get, store) => ({
count: 0,
})),
)

단지, 기존 zustand store 에 setreset 함수를 추가하는 것이기 때문에, 기존 store 생성 함수와 완전히 동일하게 사용할 수 있습니다.

import { withSetter } from '@toktokhan-dev/zustand-with-setter'

type Store = {
count: number
complexUpdate: (count: number ) => void
}

const useStore = create(
withSetter<Store>((set, get, store) => ({
count: 0,
complexUpdate: (count: number) => set((prev) => ({ count: prev.count + count * 2 })),
})),
)

const complexUpdate = useStore((store) => store.complexUpdate)
const set = useStore((store) => store.set)

set('count', 1)
complexUpdate(5)

Set

middleware 가 제공하는 업데이트 함수입니다. 기존 store 값과 마찬가지로, 접근이 가능합니다.

const set = useStore((store) => store.set)

set(store: Partial<Store>): void

전체 상태를 업데이트 할 수 있습니다. 얕은 병합을 통해 업데이트 하기 때문에 Partial 한 상태를 전달할 수 있습니다.

type Store = {
count: number
nested: { count: number; list: string[] }
}

set({ count: 5, nested: { count: 5, list: [] }})
// or
set({ count: 5 })

set(updater: (prev: Store) => Partial<Store>): void

updater 함수를 전달하여 상태 변경이 가능합니다.

type Store = {
count: number
nested: { count: number; list: string[] }
}

set((prev) => ({ ...prev, count: prev.count + 1 }))
// or
set((prev) => ({ count: prev.count + 1 }))

set(key: Key, value: Value ): void

첫번째 인자에 key 를 전달하면 해당 key에 대한 상태만 업데이트 할 수 있습니다.

set('count', 5)

중첩된 객체의 key 는 . 구분자와 함께 전달할 수 있습니다.

set('nested.count', 5)
set('nested.list', ["item-1"])
set('nested.list.1', "item-2") // ["item-1", "item-2"]

set(key: Key, updater: (prev: Value) => Value ): void

역시 updater 함수를 사용해 수정할 수 있습니다.

set('nested.list', (prev) => [...prev, 'item-3']) // ["item-1", "item-2", "item-3"]

Reset

middleware 가 제공하는 초기화 함수입니다. 기존 store 값과 마찬가지로, 접근이 가능합니다.

const reset = useStore((store) => store.reset)

reset(): void

처음 전달 받은 initialState로 상태를 초기화합니다.

reset() 

reset(key: Key): void

key 를 전달하면 해당 key에 대한 상태만 초기화 할 수 있습니다.

reset('count')
reset('nested.count')

reset(value: Partial<Store>): void

값을 전달하면, 해당 값이 적용된 상태로 초기화 할 수 있습니다.

reset({ count: 5 })

Note

Type Generic

아래처럼 create 함수에 Store 타입을 전달할 경우 create withSetter 가 생성하는 Store 타입은 set, reset 함수를 포함하기 때문에 에러가 발생합니다.

type Store = {
count: number
}

// ❌ Bad: Type Error
const useStore = create<Store>(
withSetter((set, get, store) => ({
count: 0,
})),
)

따라서, store 의 타입 정의를 위한 create 함수의 첫번째 Generic type 에 경우 create 함수가 아닌 withSetter 의 첫번째 Generic Type 으로 정의하여 사용합니다.

type Store = {
count: number
}

// ✅ Good
const useStore = create(
withSetter<Store>((set, get, store) => ({
count: 0,
})),
)