이번 포스트에서도 이어서 https://www.heropy.dev/p/n74Tgc
Zustand 핵심 정리
Zustand(주스탠드)는 작고 빠르며 확장 가능한 React 프로젝트에서 사용하는 상태 관리(Store) 라이브러리입니다.
www.heropy.dev
를 참조하여 글을 작성해보고자 한다.
상태 삭제
set 함수의 두번째 인수(기본값 false)로 true를 전달하면 상태를 병합하지 않고 덮어쓴다.
Lodash의 omit 함수는 특정 속성들을 제외한 새로운 객체를 반환한다.
이를 조합하면 특정 상태들을 삭제할 수 있다.
npm i lodash-es
npm i -D @types/lodash-es
// src/store/count.ts
import { create } from 'zustand'
import { omit } from 'lodash-es'
interface State {
count: number
double: number
min: number
max: number
}
interface Actions {
actions: {
increase: () => void
decrease: () => void
deleteState: (keys: Array<keyof State>) => void
}
}
const initialState: State = {
count: 1,
double: 2,
min: 0,
max: 99
}
export const useCountStore = create<State & Actions>(set => ({
...initialState,
actions: {
increase: () => set(state => ({ count: state.count + 1 })),
decrease: () => set(state => ({ count: state.count - 1 })),
deleteState: keys => {
set((state) => omit(state, keys), true)
}
}
}))
다음과 같이 삭제할 상태 목록을 전달해 액션을 호출한다.
import { useCountStore } from './store/count'
export default function DeleteState() {
const { deleteState } = useCountStore(state => state.actions)
return (
<>
<button onClick={() => deleteState(['min', 'max'])}>
Delete Min, Max!
</button>
</>
)
}
미들웨어
Zustand는 미들웨어라는 것을 사용해, 스토어의 추가 기능(타입 추론, 중첩 객체 변경 등)을 확장할 수 있다.
다중 미들웨어를 작성할 때는 일부 중첩 순서가 중요할 수 있다.
// 미들웨어 없이
create(콜백)
// 단일 미들웨어
import { 미들웨어 } from '미들웨어'
create{
미들웨어(콜백)
}
// 다중 미들웨어
import { 미들웨어A } from '미들웨어A'
import { 미들웨어B } from '미들웨어B'
import { 미들웨어C } from '미들웨어C'
create(
미들웨어A(
미들웨어B(
미들웨어C(콜백)
)
)
)
// 타입을 사용하는 경우
create(
미들웨어A(
미들웨어B(
미들웨어C<타입>(콜백)
)
)
)
상태의 타입 추론(Combine)
타입 스크립트를 사용할때, 상태 타입을 직접 작성하지 않고 추론하도록 combine 미들웨어를 사용할 수 있다.
combine 미들웨어는 첫번째 인수로 추론할 상태를 받고, 두 번째 인수로 set, get 매개변수를 포함하는 액션 함수를 받는다
아래 코드는 이전에 작성한 코드를 combine으로 상태 추론하도록 수정한 코드이다
import { create } from 'zustand'
import { combine } from 'zustand/middleware'
import { omit } from 'lodash-es'
const initialState = {
count: 1,
double: 2,
min: 0,
max: 99
}
export const useCountStore = create(
combine(
initialState,
set => ({
actions: {
increase: () => set(state => ({ count: state.count + 1 })),
decrease: () => set(state => ({ count: state.count - 1 })),
deleteState: (keys: Array<keyof typeof initialState>) => {
set((state) => omit(state, keys), true)
}
//..
Immer 라이브러리를 적용한다면 다음과 같이 작성할 수 있다.
import { create } from 'zustand'
import { combine } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
const initialState = {
count: 1,
double: 2
}
export const useCountStore = create(
immer(
combine(
initialState,
set => ({
actions: {
increase: () => set(state => { state.count += 1 }),
decrease: () => set(state => { state.count -= 1 })
}
// ...
이때 추론 가능하지 않은 타입은 직접 작성해야 한다.
타입 만족(satisfies)이나 타입 단언(as) 키워드를 활용해 타입을 작성하면 된다
액션 호출 in 액션
// ...
export const useCountStore = create(
combine(
initialState,
set => {
function increase() {
set(state => ({ count: state.count + 1}))
increaseDouble()
}
function increaseDouble() {
set(state => ({ double: state.count * 2 }))
}
return {
actions: {
increase,
increaseDouble
}
// ..
중첩된 객체 변경 (Immer)
다음 예시와 같이, user 객체(상태)에서 displayName 속성만 변경하는 액션을 작성할 수 있다.
여기서 set 함수는 상태 자체를 변경해야 하므로, 특정 객체에서 일부 속성만 변경하려면 새로운 객체를 할당하고 기존 속성은 복사해야 한다.
다중 중첩 객체에서 특정 하위 속성을 변경하려면 복잡한데, Immer 미들웨어를 사용하면 쉽게 변경할 수 있다.
아래의 명령어로 설치한다
npm i immer
아래 코드는 Immer 미들웨어를 적용하여
set 함수의 콜백에서 객체의 속성에 직접 접근하여 값을 변경할 수 있게 되었고, 병합할 상태 객체의 반환이 없어도 된다
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
export const useUserStore = create(
immer<State & Actions>(set => ({
...initialState,
actions: {
// ...
setDisplayName: name => {
set(state => {
if (state.user) {
state.user.displayname = name
}
}
})
'Zustand 공식 홈페이지 따라하기' 카테고리의 다른 글
Zustand 공식 홈페이지 따라하기 (3) - 블로그 참조하기 (0) | 2025.02.04 |
---|---|
Zustand 공식 홈페이지 따라하기 (1) - 블로그 참조하기 (0) | 2025.02.04 |