Zustand 基础封装
Zustand 基础封装以及nextjs使用补充
Next.js + Zustand 状态管理最佳实践
目录
高级封装方案
核心封装实现
import type { StateCreator } from 'zustand'
import type { DevtoolsOptions, PersistOptions } from 'zustand/middleware'
import { createStore as createStoreFunction } from 'zustand'
import { devtools, persist, redux, subscribeWithSelector } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
/**
* 创建包含订阅、immer 以及 devtools 功能的普通状态商店
* @param creator 状态创建器
* @param devtoolsOptions DevTools 配置选项
* @returns Zustand store
*/
export const createStore = <T extends object>(
creator: StateCreator<
T,
[
['zustand/subscribeWithSelector', never],
['zustand/immer', never],
['zustand/devtools', never],
]
>,
devtoolsOptions?: DevtoolsOptions
) => {
return createStoreFunction<T>()(subscribeWithSelector(immer(devtools(creator, devtoolsOptions))))
}
/**
* 创建包含订阅、immer 以及 devtools 功能的持久化状态商店
* 同时支持自动存储到客户端,默认存储到 localStorage
* @param creator 状态创建器
* @param persistOptions 持久化配置选项
* @param devtoolsOptions DevTools 配置选项
* @returns Zustand store
*/
export const createPersistStore = <T extends object, P = T>(
creator: StateCreator<
T,
[
['zustand/subscribeWithSelector', never],
['zustand/immer', never],
['zustand/devtools', never],
['zustand/persist', P],
]
>,
persistOptions: PersistOptions<T, P>,
devtoolsOptions?: DevtoolsOptions
) => {
return createStoreFunction<T>()(
subscribeWithSelector(
immer(devtools(persist(creator as unknown as any, persistOptions), devtoolsOptions))
)
)
}
/**
* 创建包含订阅、immer 以及 devtools 功能的 reducer 状态商店
* 支持 Redux 风格的状态管理
* @param reducer 状态更新函数
* @param initialState 初始状态
* @param devtoolsOptions DevTools 配置选项
* @returns Zustand store
*/
export const createReduxStore = <
T extends object,
A extends {
type: string
},
>(
reducer: (state: T, action: A) => T,
initialState: T,
devtoolsOptions?: DevtoolsOptions
) =>
createStoreFunction(
subscribeWithSelector(immer(devtools(redux(reducer, initialState), devtoolsOptions)))
)
/**
* 创建包含订阅、immer 以及 devtools 功能的持久化 reducer 状态商店
* 支持 Redux 风格的状态管理,同时支持持久化
* @param reducer 状态更新函数
* @param initialState 初始状态
* @param persistOptions 持久化配置选项
* @param devtoolsOptions DevTools 配置选项
* @returns Zustand store
*/
export const createPersistReduxStore = <
T extends object,
A extends {
type: string
},
P = T,
>(
reducer: (state: T, action: A) => T,
initialState: T,
persistOptions: PersistOptions<T, P>,
devtoolsOptions?: DevtoolsOptions
) =>
createStoreFunction(
subscribeWithSelector(
immer(devtools(persist(redux(reducer, initialState), persistOptions as any), devtoolsOptions))
)
)
类型定义补充
// src/libs/store/types.ts
import type { StateCreator, StoreApi } from 'zustand'
import type { DevtoolsOptions, PersistOptions } from 'zustand/middleware'
/**
* 基础 Store 类型(无持久化)
*/
export type BaseStoreCreator<T> = StateCreator<
T,
[['zustand/subscribeWithSelector', never], ['zustand/immer', never], ['zustand/devtools', never]]
>
/**
* 持久化 Store 类型
*/
export type PersistStoreCreator<T, P = T> = StateCreator<
T,
[
['zustand/subscribeWithSelector', never],
['zustand/immer', never],
['zustand/devtools', never],
['zustand/persist', P],
]
>
/**
* Redux Action 类型
*/
export interface ReduxAction {
type: string
[key: string]: any
}
/**
* Redux Reducer 类型
*/
export type ReduxReducer<T, A extends ReduxAction> = (state: T, action: A) => T
/**
* Store 实例类型
*/
export type StoreInstance<T> = StoreApi<T>
使用示例补充
1. 基础 Store 使用
// src/stores/counter.ts
import { createStore } from '@/libs/store'
interface CounterState {
count: number
increment: () => void
decrement: () => void
}
export const useCounterStore = createStore<CounterState>(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'Counter Store',
enabled: process.env.NODE_ENV === 'development',
}
)
2. 持久化 Store 使用
// src/stores/user.ts
import { createPersistStore } from '@/libs/store'
interface UserState {
user: {
id: string
name: string
email: string
} | null
setUser: (user: UserState['user']) => void
logout: () => void
}
export const useUserStore = createPersistStore<UserState>(
(set) => ({
user: null,
setUser: (user) => set({ user }),
logout: () => set({ user: null }),
}),
{
name: 'user-storage',
partialize: (state) => ({ user: state.user }), // 只持久化 user 字段
},
{
name: 'User Store',
enabled: process.env.NODE_ENV === 'development',
}
)
3. Redux 风格 Store 使用
// src/stores/todo.ts
import { createReduxStore } from '@/libs/store'
interface TodoState {
todos: Array<{ id: string; text: string; completed: boolean }>
}
interface TodoAction {
type: 'ADD_TODO' | 'TOGGLE_TODO' | 'REMOVE_TODO'
payload?: any
}
const todoReducer: ReduxReducer<TodoState, TodoAction> = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [
...state.todos,
{
id: Date.now().toString(),
text: action.payload.text,
completed: false,
},
],
}
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload.id ? { ...todo, completed: !todo.completed } : todo
),
}
case 'REMOVE_TODO':
return {
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload.id),
}
default:
return state
}
}
const initialState: TodoState = {
todos: [],
}
export const useTodoStore = createReduxStore<TodoState, TodoAction>(todoReducer, initialState, {
name: 'Todo Store',
enabled: process.env.NODE_ENV === 'development',
})
// 创建 action creators
export const todoActions = {
addTodo: (text: string) => ({ type: 'ADD_TODO' as const, payload: { text } }),
toggleTodo: (id: string) => ({ type: 'TOGGLE_TODO' as const, payload: { id } }),
removeTodo: (id: string) => ({ type: 'REMOVE_TODO' as const, payload: { id } }),
}
// 在组件中使用
export const useTodoActions = () => {
const store = useTodoStore.getState()
return {
addTodo: (text: string) => store.dispatch(todoActions.addTodo(text)),
toggleTodo: (id: string) => store.dispatch(todoActions.toggleTodo(id)),
removeTodo: (id: string) => store.dispatch(todoActions.removeTodo(id)),
}
}
4. 持久化 Redux Store 使用
// src/stores/settings.ts
import { createPersistReduxStore } from '@/libs/store'
interface SettingsState {
theme: 'light' | 'dark'
language: 'zh' | 'en'
notifications: boolean
}
interface SettingsAction {
type: 'SET_THEME' | 'SET_LANGUAGE' | 'TOGGLE_NOTIFICATIONS'
payload?: any
}
const settingsReducer: ReduxReducer<SettingsState, SettingsAction> = (state, action) => {
switch (action.type) {
case 'SET_THEME':
return { ...state, theme: action.payload.theme }
case 'SET_LANGUAGE':
return { ...state, language: action.payload.language }
case 'TOGGLE_NOTIFICATIONS':
return { ...state, notifications: !state.notifications }
default:
return state
}
}
const initialState: SettingsState = {
theme: 'light',
language: 'zh',
notifications: true,
}
export const useSettingsStore = createPersistReduxStore<SettingsState, SettingsAction>(
settingsReducer,
initialState,
{
name: 'settings-storage',
partialize: (state) => ({
theme: state.theme,
language: state.language,
notifications: state.notifications,
}),
},
{
name: 'Settings Store',
enabled: process.env.NODE_ENV === 'development',
}
)
高级特性说明
1. 类型安全
// 完整的类型推导
const store = createStore<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
// TypeScript 会自动推导出正确的类型
const { count, increment } = store.getState()
2. 中间件组合优势
// 中间件按顺序组合,每个都有特定作用
subscribeWithSelector(immer(devtools(creator)))
// subscribeWithSelector: 提供选择性订阅
// immer: 允许直接修改状态
// devtools: 提供开发者工具支持
3. Redux 兼容性
// 支持传统的 Redux 模式
const store = createReduxStore(reducer, initialState)
// 可以使用标准的 Redux 工具
store.dispatch(action)
store.getState()
store.subscribe(listener)
4. 性能优化
// 使用 subscribeWithSelector 进行精确订阅
const store = useStore.getState()
store.subscribe(
(state) => state.todos, // 只订阅 todos 变化
(todos) => {
console.log('Todos updated:', todos)
}
)
总结
这个高级封装方案的优势:
- 类型安全:完整的 TypeScript 中间件类型支持
- 性能优化:中间件按最优顺序组合
- Redux 兼容:支持传统的 reducer 模式
- 开发体验:内置 DevTools 和 Immer 支持
- 灵活性:支持持久化和非持久化两种模式
- 代码简洁:相比之前的方案更加直接和高效
这种封装方式特别适合需要 Redux 兼容性的项目,同时保持了 Zustand 的简洁性。