Redux 太重了,用 Zustand 几行代码把状态管起来

举报
茉莉风铃 发表于 2026/10/20 14:34:43 2026/10/20
【摘要】 Redux 模板代码多、Context 嵌套深,换成 Zustand 几行代码建 Store,含持久化存储、异步 action、跨组件通信,附三者对比。

React项目里状态管理一直是个绕不开的话题。Redux太重,Context嵌套多了性能拉胯,MobX上手有门槛。Zustand这个小库用起来就一个字,爽。API极少,没有模板代码,TypeScript支持也好,几行代码就能跑起来。

装包和建Store

npm install zustand

定义一个购物车store:

import { create } from 'zustand'

interface CartItem {
  id: number
  name: string
  price: number
  qty: number
}

interface CartStore {
  items: CartItem[]
  addItem: (item: Omit<CartItem, 'qty'>) => void
  removeItem: (id: number) => void
  updateQty: (id: number, qty: number) => void
  clear: () => void
  total: () => number
}

const useCartStore = create<CartStore>((set, get) => ({
  items: [],
  
  addItem: (item) => set((state) => {
    const existing = state.items.find(i => i.id === item.id)
    if (existing) {
      return {
        items: state.items.map(i =>
          i.id === item.id ? { ...i, qty: i.qty + 1 } : i
        )
      }
    }
    return { items: [...state.items, { ...item, qty: 1 }] }
  }),
  
  removeItem: (id) => set((state) => ({
    items: state.items.filter(i => i.id !== id)
  })),
  
  updateQty: (id, qty) => set((state) => ({
    items: state.items.map(i =>
      i.id === id ? { ...i, qty: Math.max(0, qty) } : i
    )
  })),
  
  clear: () => set({ items: [] }),
  
  total: () => get().items.reduce((sum, i) => sum + i.price * i.qty, 0),
}))

create函数接收一个初始化函数,参数是set和get。set改状态,get读当前状态。没有reducer、没有action type、没有dispatch,直接调函数就改了。

组件里用

function ProductList() {
  const addItem = useCartStore(s => s.addItem)
  
  const products = [
    { id: 1, name: '键盘', price: 299 },
    { id: 2, name: '鼠标', price: 99 },
  ]
  
  return (
    <div>
      {products.map(p => (
        <div key={p.id}>
          <span>{p.name} - ¥{p.price}</span>
          <button onClick={() => addItem(p)}>加入购物车</button>
        </div>
      ))}
    </div>
  )
}

function Cart() {
  const items = useCartStore(s => s.items)
  const total = useCartStore(s => s.total)
  const updateQty = useCartStore(s => s.updateQty)
  
  if (items.length === 0) return <p>购物车空空如也</p>
  
  return (
    <div>
      {items.map(item => (
        <div key={item.id}>
          <span>{item.name} × {item.qty}</span>
          <button onClick={() => updateQty(item.id, item.qty + 1)}>+</button>
          <button onClick={() => updateQty(item.id, item.qty - 1)}>-</button>
        </div>
      ))}
      <p>合计:¥{total()}</p>
    </div>
  )
}

注意组件里用useCartStore(s => s.items)选择器写法,只订阅items的变化。如果写useCartStore()拿整个store,任何状态变了组件都会重渲染,性能就白瞎了。

持久化存储

刷新页面状态丢了?加个persist中间件:

import { persist } from 'zustand/middleware'

const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      // ...同上
    }),
    { name: 'cart-storage' }
  )
)

自动存到localStorage,key叫cart-storage。刷新页面状态还在,关浏览器也在。想存sessionStorage改个storage参数就行。

Zustand vs Redux vs Context对比

特性 Zustand Redux Toolkit Context API
包体积 ~1KB ~15KB 0(内置)
模板代码 几乎没有 较多 中等
TypeScript支持 优秀 优秀 一般
性能 选择器精准更新 需要selector 全量重渲染
中间件 persist/devtools 丰富 无
学习成本 很低 中等 低
适合场景 中小型应用 大型复杂应用 简单共享状态

小到中型项目选Zustand够用了,大型项目多人协作Redux的约束力还是有价值的。Context适合那种一次设置全局只读的场景,比如主题、语言。

异步action处理

Zustand里处理异步不用thunk、不用saga,直接写async函数:

interface UserStore {
  user: User | null
  loading: boolean
  fetchUser: (id: number) => Promise<void>
}

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

组件里useUserStore(s => s.fetchUser)拿到函数,useEffect里调一下就完事。

跨组件通信

多个组件共享一个store天然支持,Zustand是全局单例。但有时候你想让某个组件局部使用store,可以用createStore创建独立实例:

import { createStore, useStore } from 'zustand'

const counterStore = createStore(() => ({ count: 0 }))

function Counter() {
  const count = useStore(counterStore, s => s.count)
  return <button onClick={() => counterStore.setState(s => ({ count: s.count + 1 }))}>
    {count}
  </button>
}

这样每个组件实例可以有自己的store,互不干扰。

Zustand的哲学就是少即是多。没有reducer、没有action creator、没有dispatch,一个create函数搞定状态定义和修改。TypeScript类型推导也顺滑,基本不用手写类型注解。项目里用了一个月,最大的感受是心智负担小,写状态管理的代码跟写普通函数没太大区别。

标签: React,TypeScript,Zustand,前端,状态管理

总结

Zustand 把状态管理从「写一堆模板代码」变回「定义一个函数」:create 建 store,组件里当 hook 用,要落盘加个 persist 中间件就够。它的定位很明确——中小型项目的状态容器,真遇到复杂数据流和中间件需求,Redux 那套还是有它的价值。挑之前先想清楚状态有多复杂,别为了轻而轻。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。