5月28日 02:37

如何创建和使用 Zustand store?

核心答案

Zustand 通过 create 函数创建 store,返回一个可直接在组件中使用的 Hook。与 Redux 不同,它不需要 Provider 包裹,store 本身就是 Hook:

javascript
import { create } from 'zustand' const useStore = create((set, get) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), reset: () => set({ count: 0 }), }))

组件中使用时,推荐通过选择器订阅,避免不必要的重渲染:

javascript
const count = useStore((state) => state.count) // 只订阅 count const increment = useStore((state) => state.increment) // 只订阅 increment

set 与 get 的用法

set 用于更新状态,支持对象和函数两种形式。Zustand 自动浅合并第一层属性,所以不需要手动展开 ...state

javascript
const useStore = create((set) => ({ user: { name: 'Tom', age: 20 }, // 对象形式:直接替换第一层属性 setName: (name) => set({ user: { name, age: 20 } }), // 注意:第二层需手动处理 // 函数形式:基于旧状态计算 incrementAge: () => set((state) => ({ user: { ...state.user, age: state.user.age + 1 } })), }))

get 用于在 action 中读取当前状态,不触发订阅:

javascript
const useStore = create((set, get) => ({ items: [], addItem: (item) => set({ items: [...get().items, item] }), getCount: () => get().items.length, // 不触发重渲染 }))

选择性订阅与性能优化

直接解构整个 store 会导致任何状态变化都触发重渲染,应避免:

javascript
// 不推荐:任何状态变化都触发重渲染 const { count, name } = useStore() // 推荐:按需订阅 const count = useStore((s) => s.count) const name = useStore((s) => s.name)

对于复杂对象,使用 shallow 比较避免引用变化导致的重渲染:

javascript
import { shallow } from 'zustand/shallow' const { name, age } = useStore( (s) => ({ name: s.user.name, age: s.user.age }), shallow )

Store 拆分(Slice 模式)

大型应用中,将不同领域的状态拆成独立 slice,再合并到一个 store:

javascript
// slices/cartSlice.js export const createCartSlice = (set) => ({ items: [], addItem: (item) => set((s) => ({ items: [...s.items, item] })), clearCart: () => set({ items: [] }), }) // slices/userSlice.js export const createUserSlice = (set) => ({ user: null, setUser: (user) => set({ user }), }) // store.js import { create } from 'zustand' import { createCartSlice } from './slices/cartSlice' import { createUserSlice } from './slices/userSlice' const useStore = create((...a) => ({ ...createCartSlice(...a), ...createUserSlice(...a), }))

异步操作

Zustand 的 action 可以直接是 async 函数,不需要额外的中间件:

javascript
const useStore = create((set) => ({ data: null, loading: false, error: null, fetchData: async (id) => { set({ loading: true, error: null }) try { const res = await fetch(`/api/data/${id}`) const data = await res.json() set({ data, loading: false }) } catch (error) { set({ error: error.message, loading: false }) } }, }))

常用中间件

persist — 持久化到 localStorage

javascript
import { create } from 'zustand' import { persist } from 'zustand/middleware' const useStore = create( persist( (set) => ({ theme: 'light', setTheme: (theme) => set({ theme }), }), { name: 'theme-storage' } // localStorage key ) )

immer — 不可变更新的简化写法

javascript
import { create } from 'zustand' import { immer } from 'zustand/middleware/immer' const useStore = create( immer((set) => ({ user: { name: 'Tom', address: { city: 'Beijing' } }, setCity: (city) => set((state) => { state.user.address.city = city }), // 无需手动展开,直接修改 draft })) )

devtools — Redux DevTools 调试支持

javascript
import { create } from 'zustand' import { devtools } from 'zustand/middleware' const useStore = create( devtools((set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })), }), { name: 'CounterStore' }) )

中间件可以组合使用,顺序从外到内:devtools(persist(immer(...)))

create 与 createStore 的区别

createcreateStore
返回值React HookStore 对象
使用场景React 组件内React 外(测试、服务端、非React环境)
订阅方式useStore(s => s.xxx)store.subscribe() / store.getState()
javascript
import { createStore } from 'zustand' const store = createStore((set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })), })) // React 外部使用 store.getState().count // 读取 store.setState({ count: 10 }) // 更新 store.subscribe((state) => { // 监听 console.log('state changed', state) })

追问:Zustand 与 Redux 的核心区别是什么?

  1. 无需 Provider:Zustand 不需要 <Provider> 包裹组件树,直接导入 Hook 使用
  2. 订阅粒度:Zustand 通过选择器精确订阅,Redux 用 useSelector 实现类似效果但机制不同
  3. 样板代码:Zustand 无 action type、reducer、dispatch,一个函数搞定
  4. Bundle 体积:Zustand ~1KB vs Redux Toolkit ~11KB
  5. 中间件生态:Redux 有更成熟的中间件链,Zustand 的中间件更轻量但够用
标签:ReactZustand