Zustand中间件技能Skill zustand-middleware

Zustand中间件技能用于实现JavaScript状态管理库Zustand的中间件功能,包括持久化存储、Redux DevTools集成、使用Immer进行不可变更新和自定义中间件开发。适用于前端开发中的状态管理优化,提高应用性能和可维护性。关键词:Zustand, 中间件, 持久化, DevTools, Immer, 前端开发, 状态管理, 不可变更新, 自定义中间件

前端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称: zustand-middleware 用户可调用: false 描述: 当实现Zustand中间件以增强存储功能,包括持久化、开发工具、不可变性等时使用。涵盖持久化、devtools、immer和自定义中间件。 允许工具:

  • 读取
  • 写入
  • 编辑
  • Bash
  • Grep
  • Glob

Zustand - 中间件

Zustand提供强大的中间件来增强存储功能,包括持久化存储、Redux DevTools集成、使用Immer进行不可变更新等。

关键概念

中间件组合

中间件包装存储创建函数:

import { create } from 'zustand'
import { persist, devtools } from 'zustand/middleware'

const useStore = create(
  devtools(
    persist(
      (set) => ({
        count: 0,
        increment: () => set((state) => ({ count: state.count + 1 })),
      }),
      { name: 'counter-storage' }
    )
  )
)

顺序重要

从内到外应用中间件:

// ✅ 正确顺序
create(devtools(persist(immer(...))))

// devtools包装persist包装immer包装你的存储

最佳实践

1. 持久化中间件

保存和恢复存储状态到localStorage或其他存储:

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface CartStore {
  items: CartItem[]
  addItem: (item: CartItem) => void
  removeItem: (id: string) => void
  clearCart: () => void
}

const useCartStore = create<CartStore>()(
  persist(
    (set) => ({
      items: [],
      addItem: (item) =>
        set((state) => ({ items: [...state.items, item] })),
      removeItem: (id) =>
        set((state) => ({
          items: state.items.filter((item) => item.id !== id),
        })),
      clearCart: () => set({ items: [] }),
    }),
    {
      name: 'shopping-cart',
      storage: createJSONStorage(() => localStorage),
    }
  )
)

持久化选项

persist(
  (set) => ({ /* 存储 */ }),
  {
    name: 'my-store', // 存储键的唯一名称
    storage: createJSONStorage(() => localStorage), // 或sessionStorage
    partialize: (state) => ({ count: state.count }), // 仅持久化特定字段
    onRehydrateStorage: (state) => {
      console.log('水合开始')
      return (state, error) => {
        if (error) {
          console.log('水合期间错误', error)
        } else {
          console.log('水合完成')
        }
      }
    },
    version: 1,
    migrate: (persistedState, version) => {
      // 处理版本迁移
      if (version === 0) {
        // 将旧状态迁移到新格式
      }
      return persistedState
    },
  }
)

2. DevTools中间件

与Redux DevTools集成进行调试:

import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

interface Store {
  count: number
  increment: () => void
  decrement: () => void
}

const useStore = create<Store>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set((state) => ({ count: state.count + 1 }), false, 'increment'),
      decrement: () =>
        set((state) => ({ count: state.count - 1 }), false, 'decrement'),
    }),
    { name: 'CounterStore' }
  )
)

DevTools选项

devtools(
  (set) => ({ /* 存储 */ }),
  {
    name: 'MyStore', // 在devtools中的名称
    enabled: process.env.NODE_ENV === 'development', // 有条件启用
    anonymousActionType: 'action', // 默认操作名称
    trace: true, // 包含堆栈跟踪
  }
)

3. Immer中间件

使用可变语法编写不可变更新:

import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface TodoStore {
  todos: Todo[]
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
  updateTodo: (id: string, text: string) => void
}

const useTodoStore = create<TodoStore>()(
  immer((set) => ({
    todos: [],

    addTodo: (text) =>
      set((state) => {
        state.todos.push({
          id: Date.now().toString(),
          text,
          completed: false,
        })
      }),

    toggleTodo: (id) =>
      set((state) => {
        const todo = state.todos.find((t) => t.id === id)
        if (todo) {
          todo.completed = !todo.completed
        }
      }),

    updateTodo: (id, text) =>
      set((state) => {
        const todo = state.todos.find((t) => t.id === id)
        if (todo) {
          todo.text = text
        }
      }),
  }))
)

4. 订阅

在React外部监听状态变化:

const useStore = create<Store>()((set) => ({ /* ... */ }))

// 订阅所有变化
const unsubscribe = useStore.subscribe((state, prevState) => {
  console.log('状态变化:', state)
})

// 订阅特定值
const unsubscribe = useStore.subscribe(
  (state) => state.count,
  (count, prevCount) => {
    console.log('计数从', prevCount, '变为', count)
  }
)

// 清理
unsubscribe()

5. 组合多个中间件

import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'

interface Store {
  count: number
  todos: Todo[]
  increment: () => void
  addTodo: (text: string) => void
}

const useStore = create<Store>()(
  devtools(
    persist(
      immer((set) => ({
        count: 0,
        todos: [],

        increment: () =>
          set((state) => {
            state.count++
          }),

        addTodo: (text) =>
          set((state) => {
            state.todos.push({
              id: Date.now().toString(),
              text,
              completed: false,
            })
          }),
      })),
      {
        name: 'app-storage',
        partialize: (state) => ({
          count: state.count,
          todos: state.todos,
        }),
      }
    ),
    { name: 'AppStore' }
  )
)

示例

自定义日志中间件

import { StateCreator, StoreMutatorIdentifier } from 'zustand'

type Logger = <
  T,
  Mps extends [StoreMutatorIdentifier, unknown][] = [],
  Mcs extends [StoreMutatorIdentifier, unknown][] = []
>(
  f: StateCreator<T, Mps, Mcs>,
  name?: string
) => StateCreator<T, Mps, Mcs>

type LoggerImpl = <T>(
  f: StateCreator<T, [], []>,
  name?: string
) => StateCreator<T, [], []>

const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => {
  const loggedSet: typeof set = (...a) => {
    set(...a)
    console.log(...(name ? [`${name}:`] : []), get())
  }

  store.setState = loggedSet

  return f(loggedSet, get, store)
}

export const logger = loggerImpl as unknown as Logger

// 用法
const useStore = create<Store>()(
  logger(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    'CounterStore'
  )
)

自定义重置中间件

import { StateCreator, StoreMutatorIdentifier } from 'zustand'

type Resettable = <
  T,
  Mps extends [StoreMutatorIdentifier, unknown][] = [],
  Mcs extends [StoreMutatorIdentifier, unknown][] = []
>(
  f: StateCreator<T, Mps, Mcs>
) => StateCreator<T, Mps, Mcs>

type ResettableImpl = <T>(
  f: StateCreator<T, [], []>
) => StateCreator<T, [], []>

const resettableImpl: ResettableImpl = (f) => (set, get, store) => {
  const initialState = f(set, get, store)

  store.reset = () => set(initialState)

  return initialState
}

export const resettable = resettableImpl as unknown as Resettable

// 扩展存储类型
declare module 'zustand' {
  interface StoreApi<T> {
    reset?: () => void
  }
}

// 用法
const useStore = create<Store>()(
  resettable((set) => ({
    count: 0,
    name: '',
    increment: () => set((state) => ({ count: state.count + 1 })),
    setName: (name) => set({ name }),
  }))
)

// 重置到初始状态
useStore.reset()

IndexedDB持久化

import { StateStorage } from 'zustand/middleware'
import { get, set, del } from 'idb-keyval'

const indexedDBStorage: StateStorage = {
  getItem: async (name: string): Promise<string | null> => {
    return (await get(name)) || null
  },
  setItem: async (name: string, value: string): Promise<void> => {
    await set(name, value)
  },
  removeItem: async (name: string): Promise<void> => {
    await del(name)
  },
}

const useStore = create<Store>()(
  persist(
    (set) => ({
      largeData: [],
      addData: (data) =>
        set((state) => ({ largeData: [...state.largeData, data] })),
    }),
    {
      name: 'large-data-storage',
      storage: createJSONStorage(() => indexedDBStorage),
    }
  )
)

React Native的异步存储

import AsyncStorage from '@react-native-async-storage/async-storage'
import { StateStorage } from 'zustand/middleware'

const asyncStorage: StateStorage = {
  getItem: async (name: string): Promise<string | null> => {
    return await AsyncStorage.getItem(name)
  },
  setItem: async (name: string, value: string): Promise<void> => {
    await AsyncStorage.setItem(name, value)
  },
  removeItem: async (name: string): Promise<void> => {
    await AsyncStorage.removeItem(name)
  },
}

const useStore = create<Store>()(
  persist(
    (set) => ({ /* ... */ }),
    {
      name: 'app-storage',
      storage: createJSONStorage(() => asyncStorage),
    }
  )
)

常见模式

条件持久化

仅持久化某些字段:

const useStore = create<Store>()(
  persist(
    (set) => ({
      // 持久化
      theme: 'light',
      language: 'en',

      // 不持久化
      isLoading: false,
      error: null,

      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
    }),
    {
      name: 'settings',
      partialize: (state) => ({
        theme: state.theme,
        language: state.language,
      }),
    }
  )
)

版本迁移

处理持久化状态的破坏性变化:

const useStore = create<Store>()(
  persist(
    (set) => ({ /* ... */ }),
    {
      name: 'app-store',
      version: 2,
      migrate: (persistedState: any, version: number) => {
        if (version === 0) {
          // 从版本0迁移到1
          persistedState.newField = 'default'
        }

        if (version === 1) {
          // 从版本1迁移到2
          persistedState.items = persistedState.oldItems.map((item: any) => ({
            id: item.id,
            name: item.title, // 重命名字段
          }))
          delete persistedState.oldItems
        }

        return persistedState as Store
      },
    }
  )
)

水合检测

知道持久化状态何时加载:

const useStore = create<Store>()(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'counter',
      onRehydrateStorage: () => (state) => {
        console.log('状态水合:', state)
      },
    }
  )
)

// 在组件中
function App() {
  const [hydrated, setHydrated] = useState(false)

  useEffect(() => {
    useStore.persist.onFinishHydration(() => {
      setHydrated(true)
    })
  }, [])

  if (!hydrated) {
    return <div>加载中...</div>
  }

  return <div>应用内容</div>
}

反模式

❌ 不要持久化敏感数据

// 错误:在localStorage中持久化令牌
const useAuthStore = create(
  persist(
    (set) => ({
      token: null,
      user: null,
      login: async (credentials) => {
        const { token, user } = await api.login(credentials)
        set({ token, user }) // ❌ 令牌在localStorage中
      },
    }),
    { name: 'auth' }
  )
)

// 正确:使用安全存储或不持久化令牌
const useAuthStore = create(
  persist(
    (set) => ({
      user: null,
      login: async (credentials) => {
        const { token, user } = await api.login(credentials)
        secureStorage.setToken(token) // ✅ 安全存储
        set({ user })
      },
    }),
    {
      name: 'auth',
      partialize: (state) => ({ user: state.user }), // ✅ 仅持久化用户
    }
  )
)

❌ 不要忽略中间件顺序

// 错误:DevTools无法看到持久化的初始状态
create(persist(devtools(...)))

// 正确:DevTools可以看到完整的状态生命周期
create(devtools(persist(...)))

❌ 不要在没有Immer的情况下突变状态

// 错误:直接突变而不使用immer
const useStore = create((set) => ({
  items: [],
  addItem: (item) =>
    set((state) => {
      state.items.push(item) // ❌ 直接突变
      return state
    }),
}))

// 正确:使用immer中间件
const useStore = create(
  immer((set) => ({
    items: [],
    addItem: (item) =>
      set((state) => {
        state.items.push(item) // ✅ 使用immer安全
      }),
  }))
)

❌ 不要忘记清理订阅

// 错误:内存泄漏
useEffect(() => {
  useStore.subscribe((state) => {
    console.log(state)
  })
}, [])

// 正确:清理订阅
useEffect(() => {
  const unsubscribe = useStore.subscribe((state) => {
    console.log(state)
  })
  return unsubscribe
}, [])

相关技能

  • zustand-store-patterns: 基本存储创建和使用
  • zustand-typescript: TypeScript与中间件集成
  • zustand-advanced-patterns: 自定义中间件和高级技术