React Zustand 状态管理实践指南

7 min read
Table of Contents

Zustand 是一个轻量级、快速且可扩展的 React 状态管理库。它以其简洁的 API 和基于 Hook 的设计理念受到广泛欢迎。本文将结合实际项目代码,总结 Zustand 的基础用法以及 Immer、Persist 等高级中间件的实战应用。

1. 基础篇:快速上手

1.1 创建 Store

最基础的 Zustand 用法是通过 create 方法创建一个 Hook。Store 中包含了状态(State)和更新状态的方法(Actions)。

参考项目中的 app/store/price.ts

typescript
import { create } from 'zustand'
interface PriceStore {
price: number
state: number
increment: () => void
decrement: () => void
resetPrice: () => void
getPrice: () => number
setPrice: (price: number) => void
}
const usePriceStore = create<PriceStore>((set, get) => ({
price: 0,
state: 123,
increment: () => set((state) => ({ price: state.price + 1 })),
decrement: () => set((state) => ({ price: state.price - 1 })),
resetPrice: () => set(() => ({ price: 0 })),
getPrice: () => get().price,
setPrice: (price: number) => set(() => ({ price: price })),
}))
export default usePriceStore

1.2 在组件中使用

创建好 Store 后,可以直接在组件中调用这个 Hook 来获取状态和方法。

参考 app/pages/Zustand/index.tsx

tsx
import usePriceStore from '~/store/price'
export default function Zustand() {
const priceStore = usePriceStore()
return (
<div className="w-120 h-120 bg-[#242424] box-border">
<div className='text-white text-4xl bg-amber-300 w-30 h-40 mt-4 relative box-border'>{priceStore.price}</div>
<div className="h-40 w-full flex items-center justify-start p-4 gap-4">
<div
className="bg-blue-300 text-white w-40 h-40 flex items-center justify-center cursor-pointer"
onClick={() => priceStore.increment()}
>
+
</div>
<div
className="bg-red-300 text-white w-40 h-40 flex items-center justify-center cursor-pointer"
onClick={() => priceStore.decrement()}
>
</div>
<div
className="bg-green-300 text-white w-40 h-40 flex items-center justify-center cursor-pointer"
onClick={() => priceStore.resetPrice()}
>
重置
</div>
</div>
</div>
)
}

2. 进阶篇:中间件与复杂状态

对于更复杂的场景,Zustand 提供了丰富的中间件支持。在 app/store/hlw.ts 中,我们结合了 immerpersistsubscribeWithSelector

2.1 组合使用中间件

app/store/hlw.ts 中,展示了如何使用 createStore(Vanilla Store)并组合多个中间件:

typescript
import { createStore } from 'zustand/vanilla'
import { immer } from 'zustand/middleware/immer'
import { persist, createJSONStorage } from 'zustand/middleware'
import { subscribeWithSelector } from 'zustand/middleware'
export interface HLW {
name: '大娃' | '二娃' | '三娃' | '四娃' | '五娃' | '六娃' | '七娃' | undefined
level: '初始' | '超进化'
}
const data = [
{ name: '大娃', level: '初始' },
{ name: '二娃', level: '初始' },
{ name: '三娃', level: '初始' },
{ name: '四娃', level: '初始' },
{ name: '五娃', level: '初始' },
{ name: '六娃', level: '初始' },
{ name: '七娃', level: '初始' },
]
const kunkun = {
name: '房房',
age: 18,
gender: '',
hobbies: {
sing: '唱歌',
dance: '跳舞',
rap: 'rap',
basketball: '篮球',
},
}
interface HLWStore {
hlws: HLW[]
kunkun: typeof kunkun
updateName: (name: string) => void
updateRap: (rap: string) => void
updateBasketball: (basketball: string) => void
upgrade: (name: HLW['name']) => void
setAge: () => void
reset: () => void
}
const useHLWStore = createStore<HLWStore>()(
immer(
subscribeWithSelector(
persist(
(set) => ({
hlws: data as HLW[],
kunkun,
updateName: (name: string) => {
set((state) => {
state.kunkun.name = name
})
},
updateRap: (rap: string) => {
set((state) => {
state.kunkun.hobbies.rap = rap
})
},
updateBasketball: (basketball: string) => {
set((state) => {
state.kunkun.hobbies.basketball = basketball
})
},
upgrade: (name: HLW['name']) =>
set((state) => {
let hlw = state.hlws.find((hlw) => hlw.name === name)
hlw && (hlw.level = '超进化')
}),
reset: () =>
set((state) => {
state.hlws = data as HLW[]
state.kunkun = kunkun
}),
setAge: () => {
set((state) => {
state.kunkun.age++
})
},
}),
{
name: 'hlw',
storage: createJSONStorage(() => localStorage),
}
)
)
)
)
export default useHLWStore

在这个例子中:

  • immer 让我们能够直接修改嵌套状态(如 state.kunkun.hobbies.rap = rap)。
  • persist 将状态持久化到 localStorage 中,key 为 hlw
  • subscribeWithSelector 允许我们精确订阅 store 的一部分。
  • 使用 createStore 创建的是一个 Vanilla Store,在 React 组件中使用时需要配合 useStore hook。

2.2 订阅状态变化与 useShallow 优化

在组件 app/pages/HLW/B.tsx 中,我们展示了如何使用 useStore 结合 useShallow 进行性能优化,以及如何使用 subscribe 监听状态变化。

tsx
import { useEffect, useState } from 'react'
import { useStore } from 'zustand'
import { useShallow } from 'zustand/react/shallow'
import useHLWStore from '~/store/hlw'
export default function B() {
// 使用 useShallow 进行浅比较,避免不必要的重渲染
const { rap, basketball, age } = useStore(
useHLWStore,
useShallow((state) => ({
rap: state.kunkun.hobbies.rap,
age: state.kunkun.age,
basketball: state.kunkun.hobbies.basketball,
}))
)
const [status, setStatus] = useState<'单身' | '结婚'>('单身')
// 使用 subscribe 监听 age 的变化
useEffect(() => {
useHLWStore.subscribe(
(state) => state.kunkun.age,
(age) => {
console.log('执行了useEffect')
if (age >= 26) {
setStatus('结婚')
} else {
setStatus('单身')
}
}
)
}, [])
return (
<div className="w-100 h-50 border border-amber-700 rounded-sm p-4">
<div>
爱好:
<span>
{rap}-{basketball}
</span>
</div>
<div>
年龄:<span>{age}</span>
</div>
<div>
状态:<span>{status}</span>
</div>
</div>
)
}

2.3 操作复杂状态

组件 app/pages/HLW/A.tsx 展示了如何调用 store 中的 action 来修改复杂状态:

tsx
import { Button, Input, Select } from 'antd'
import { useState } from 'react'
import useHLWStore, { type HLW } from '~/store/hlw'
import { useStore } from 'zustand'
export default function A() {
const hlwStore = useStore(useHLWStore)
const [name, setName] = useState<HLW['name'] | undefined>()
function superAd() {
hlwStore.upgrade(name)
}
function reset() {
hlwStore.reset()
}
function updateName() {
hlwStore.updateName('房房222')
}
function updateRap() {
hlwStore.updateRap('rap222')
}
function updateBasketball() {
hlwStore.updateBasketball('篮球222')
}
function setAge() {
hlwStore.setAge()
}
return (
<div className="w-100 h-50 border border-amber-700 rounded-sm p-4">
<Button className='mr-2' onClick={superAd}>超进化</Button>
<Button className='mr-2' onClick={reset}>重置</Button>
<Select
className="w-30"
options={hlwStore.hlws.map((hlw) => ({ label: hlw.name, value: hlw.name }))}
onChange={(value) => {
setName(value)
}}
/>
<Button className='mr-2' onClick={()=>updateName()}>修改姓名</Button>
<Button className='mr-2' onClick={()=>updateRap()}>修改rap</Button>
<Button className='mr-2' onClick={()=>updateBasketball()}>修改篮球</Button>
<Button className='mr-2' onClick={()=>setAge()}>增加年龄</Button>
</div>
)
}

总结

Zustand 提供了极其灵活的状态管理方案。

  • 简单场景:直接使用 create 和基础 Actions。
  • 复杂场景:组合使用 immer 处理深层更新,persist 处理持久化。
  • 性能敏感:利用 Selector 和 useShallow 精确控制渲染。
  • 逻辑解耦:利用 subscribe 在组件外监听状态变化。

通过合理组合这些特性,我们可以构建出既高效又易于维护的 React 应用状态管理层。

后记

My avatar

感谢你读到这里。

这座小站更像一份持续维护的“终端笔记”:记录我解决问题的过程,也记录走过的弯路。

如果这篇内容对你有一点点帮助:

  • 点个赞 / 收藏一下,方便你下次回来继续翻
  • 欢迎在评论区补充你的做法(或者指出我的疏漏)
  • 想持续收到更新:可以订阅 RSS(在页面底部)

我们下篇见。


More Posts

评论

评论 (0)

请先登录后再发表评论

加载中...