React中类似于Vue中Pinia的轻量级状态管理神器——Zustand

Zustand ​ 是一个轻量级、简洁的React状态管理库,核心特点是无样板代码、hooks风格、不依赖Context。用法很像Vue生态中的Pinia,以下是详细使用指南:

一、基础使用(计数器示例)

1. 创建store

typescript 复制代码
// store/counterStore.ts
import { create } from 'zustand'

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

const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))

2. 组件中使用

javascript 复制代码
// Counter.tsx
import useCounterStore from './store/counterStore'

const Counter = () => {
  // 自动订阅状态变化 - 当count变化时组件重渲染
  const { count, increment, decrement } = useCounterStore()
  
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}

// 兄弟组件同步更新
const Display = () => {
  const count = useCounterStore((state) => state.count) // 选择性订阅
  
  return <div>Current count: {count}</div>
}

二、进阶特性

1. 异步操作

typescript 复制代码
// store/userStore.ts
interface UserState {
  user: User | null
  loading: boolean
  fetchUser: (id: string) => Promise<void>
}

const useUserStore = create<UserState>((set) => ({
  user: null,
  loading: false,
  
  fetchUser: async (id: string) => {
    set({ loading: true })
    try {
      const response = await fetch(`/api/users/${id}`)
      const user = await response.json()
      set({ user, loading: false })
    } catch (error) {
      set({ loading: false })
      throw error
    }
  },
}))

2. 深层嵌套状态更新

typescript 复制代码
// 使用immer简化嵌套更新(需安装immer)
import { produce } from 'immer'

const useTodoStore = create<{
  todos: Todo[]
  addTodo: (text: string) => void
  toggleTodo: (id: number) => void
}>((set) => ({
  todos: [],
  addTodo: (text) => set(produce((state) => {
    state.todos.push({ id: Date.now(), text, completed: false })
  })),
  toggleTodo: (id) => set(produce((state) => {
    const todo = state.todos.find(t => t.id === id)
    if (todo) todo.completed = !todo.completed
  })),
}))

3. 性能优化:选择性订阅

javascript 复制代码
// ✅ 只订阅需要的状态,避免不必要的重渲染
const TodoList = () => {
  const todos = useTodoStore((state) => state.todos) // 仅当todos变化时重渲染
  
  const addTodo = useTodoStore((state) => state.addTodo) // action不会触发重渲染
  
  return (
    <div>
      {todos.map(todo => <TodoItem key={todo.id} />)}
      <AddTodo onAdd={addTodo} />
    </div>
  )
}

// TodoItem组件 - 独立订阅
const TodoItem = ({ id }) => {
  const todo = useTodoStore(
    (state) => state.todos.find(t => t.id === id)
  )
  const toggleTodo = useTodoStore((state) => state.toggleTodo)
  
  // 每个TodoItem只在自己的todo变化时重渲染
  return <li onClick={() => toggleTodo(id)}>{todo.text}</li>
}

4. 中间件使用

javascript 复制代码
// 持久化存储(需安装zustand/middleware)
import { persist, createJSONStorage } from 'zustand/middleware'

const useAuthStore = create(
  persist(
    (set, get) => ({
      token: null,
      user: null,
      login: (credentials) => { /* ... */ },
      logout: () => set({ token: null, user: null }),
    }),
    {
      name: 'auth-storage', // localStorage key
      storage: createJSONStorage(() => localStorage),
    }
  )
)

// 开发工具中间件
import { devtools } from 'zustand/middleware'

const useStore = create(
  devtools(
    (set) => ({
      /* state & actions */
    }),
    { name: 'MyStore' } // Redux DevTools中的显示名称
  )
)

三、最佳实践

1. 拆分store(按领域划分)

bash 复制代码
store/
├── authStore.ts      # 认证相关
├── cartStore.ts      # 购物车
├── uiStore.ts        # UI状态(主题、弹窗)
└── productStore.ts   # 商品数据

2. TypeScript完整示例

typescript 复制代码
// store/authStore.ts
interface User {
  id: string
  name: string
  email: string
}

interface AuthState {
  user: User | null
  isAuthenticated: boolean
  login: (email: string, password: string) => Promise<void>
  logout: () => void
  initialize: () => Promise<void>
}

export const useAuthStore = create<AuthState>((set, get) => ({
  user: null,
  isAuthenticated: false,
  
  login: async (email: string, password: string) => {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    })
    
    const user = await response.json()
    set({ user, isAuthenticated: true })
  },
  
  logout: () => {
    localStorage.removeItem('token')
    set({ user: null, isAuthenticated: false })
  },
  
  initialize: async () => {
    const token = localStorage.getItem('token')
    if (token) {
      // 验证token并获取用户信息
      const user = await fetchCurrentUser()
      set({ user, isAuthenticated: true })
    }
  },
}))

3. 在类组件中使用(兼容方案)

javascript 复制代码
// withStore HOC
import { useCounterStore } from './store/counterStore'

const withCounterStore = (Component) => {
  return (props) => {
    const store = useCounterStore()
    return <Component {...props} store={store} />
  }
}

class LegacyComponent extends React.Component {
  render() {
    const { store } = this.props
    return <div>Count: {store.count}</div>
  }
}

export default withCounterStore(LegacyComponent)

四、对比其他方案

特性 Zustand Redux Toolkit Context
包大小 ~1KB ~8KB 内置
学习曲线 极低 中等
样板代码 极少 中等
性能 自动优化 手动优化 需要Memo
DevTools 插件支持 内置完整
异步 原生支持 RTK Query 需手动处理

五、与Pinia的开发体验对比

方面 Zustand Pinia 评价
学习成本 极低 Zustand更简单
代码简洁度 极高 Zustand更函数式
Vue集成 不适用 完美 Pinia是Vue生态一部分
React集成 原生hooks 不适用 Zustand是React专用
调试体验 需要中间件 开箱即用 Pinia胜出
社区生态 增长快 Vue官方 Pinia更稳定

六、快速开始

bash 复制代码
# 安装
npm install zustand

# 可选中间件
npm install immer @types/immer  # 不可变更新
npm install zustand/middleware  # 官方中间件

总结 :Zustand通过极简的API提供了完整的全局状态管理能力,适合大多数React项目。其核心优势是零样板、直观、高性能,避免了Redux的复杂性和Context的性能陷阱。

相关推荐
观测云10 分钟前
AI时代的用户访问监测:观测云带你身临其境体验用户与前端UI交互旅程
前端·可观测性·观测云·rum
喵本喵叁肆22 分钟前
06-M6-部门过滤与综合研判-从问答机到研判助手
前端·javascript·jquery
TomEval1 小时前
【Web UI 自动化】05 - KDT 模式原理与实现
前端·ui·自动化
南雨北斗1 小时前
vue3项目状态持久化方案Pinia
前端
计算机魔术师1 小时前
Uber 工程师不动手,Agent 接管了 70% 的代码 PR
前端
南雨北斗1 小时前
Vue3项目中使用Pinia代替TP6 session传递
前端
律宏阔1 小时前
Electron 打包 CloakBrowser:解决客户电脑缺少浏览器二进制的问题
前端
এ慕ོ冬℘゜2 小时前
jQuery 核心方法
前端·javascript·jquery
阳火锅2 小时前
老板要周报?我说不用,代码提交就是进度
前端·javascript·vue.js