React — zustand状态管理工具
分类: React 18 0
本文转自 https://juejin.cn/post/7388064351504056335,如有侵权,请联系删除。
Zustand 是目前生态系统中非常流行的一款状态管理库,相比经典的 Redux,学习成本极低,使用超简单,今天就来介绍。
快速开始
创建项目
bash 体验AI代码助手 代码解读复制代码npm create vite@latest zustand-demo -- --template react
cd zustand-demo
# VS Code 打开
code .
npm install zustand
npm install
快速使用 Zustand
删除 src/index.css 中的内容,将 src/App.jsx 中的内容替换如下:
```jsx
jsx 体验AI代码助手 代码解读复制代码import { create } from 'zustand'
const useStore = create(() => ({
who: 'World',
}))
function App() {
const who = useStore(state => state.who)
return (
<>
Hello {who}!
</>
)
}
export default App
以上,我们就写了一个最小化的 Zustand 应用了。
1. 首先,我们向 Zustand 暴露出来的 create() API 中传入了一个回调函数
1. 接着,回调函数返回了一个对象 { who: 'World' }
,这就是状态对象了,那如何用呢?这就德通过返回值了
1. 对,没错,create() 会返回一个 React Hook,通常我们会叫它,useStore() 或者是根据具体业务场景而命名的 useXXStore()
1. 最后,调用 useStore() 时,Zustand 会将返回的状态传入,由此你可解构出你需要的部分返回。本例中,我们就从 Store 中解构出了 who
这个状态属性
浏览器访问 [http://localhost:5173/](https://link.juejin.cn/?target=http%3A%2F%2Flocalhost%3A5173%2F) 查看:

发现,值为 'World'
的 who
状态被成功渲染出来了。
## 更新状态
当然除了存储状态,Zustand 还支持我们修改状态,在以上案例的基础之上,我们需要修改 2 处。
```jsx
jsx 体验AI代码助手 代码解读复制代码const useStore = create((set /* 1 */) => ({
who: 'World',
setWho: (who) => set({ who }) /* 2 */
}))
-
首先,在调用 create() API 的时候,Zustand 还会额外传入一个 set 参数,它是一个函数,用于设置状态
- create() 回调函数中除了能返回状态,还能返回操作状态的函数。本例中新增了一个 setWho() 函数,用于修改状态 who
接下来,稍稍修改 App 组件。
```jsx
jsx 体验AI代码助手 代码解读复制代码function App() {
const who = useStore(state => state.who)
const setWho = useStore(state => state.setWho) / 1 /return (
<>
<p>Hello {who}!</p>
<button onClick={() => setWho('Zustand') / 2 /}>Say Hi to Zustand</button>
</>
)
} -
引入状态修改函数 setWho()
- 增加按钮,点击时调用 setWho('Zustand'),将状态 who 更新为
'Zustand'
来看效果:
发现状态被成功修改了。
除此之外,Zustand 还天然支持局部状态更新——就是说如果你的 Store 对象中包含不止一个属性时,你也只需要更新你关心的状态即可,其他状态会自动保持原样。
举个例子。上面的例子,现在除了 who,还有一个 count。
diff 体验AI代码助手 代码解读复制代码const useStore = create((set) => ({ + count: 0, who: 'World', setWho: (who) => set({ who }) }))
上述的,setWho() 无需改变,保持 set({ who }) 即可,没被设置的 count 依然保持原样。
```diff
diff 体验AI代码助手 代码解读复制代码- <p>Hello {who}!</p>- <p>Hello {who}!(<strong>{useStore(state => state.count)}</strong>)</p>
查看效果:
发现 who 被成功更新的同时,count 状态也保留着,这就很棒了。
同样的更新方式,换成 useState(),我们就得这么做:
jsx 体验AI代码助手 代码解读复制代码const [store, setStore] = useState({ who: 'World', count: 0 }) const setWho = (who) => { setStore((prevStore) => ({ ...prevStore, who: 'Zustand' })) }
看看,是不是复杂很多,这就是使用 Zustand 的好处。
另外,set() 函数还支持回调函数更新形式,回调函数会接受当前的状态对象,这样就可以基于以前的状态进行更新了。
以以下新建的 useCountStore 为例:
```jsx
jsx 体验AI代码助手 代码解读复制代码const useCountStore = create((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })),
}))更新 count 状态的 inc() 函数内部就是通过在之前 state.count 之上加 1 的方式实现计数增加的。 App 组件修改如下: ```jsx jsx 体验AI代码助手 代码解读复制代码function App() { const count = useCountStore(state => state.count) const inc = useCountStore(state => state.inc) return ( <> <button onClick={() => inc()}>count: {count}</button> </> ) }</code></pre> <p>查看效果:</p> <p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6ff69c6470f347eb8dafa3f8e58c3309~tplv-k3u1fbpfcp-jj-mark:3024:0:0:0:q75.awebp#?w=530&h=242&s=32691&e=gif&f=19&b=f9f9f9" alt="" /></p> <p>计数按照预期的增加了。</p> <h2>更新嵌套状态</h2> <p>值得注意的是,<strong>Zustand 的局部更新只适应于第一层属性,对嵌套对象中的属性是无效的</strong>。</p> <p>我们先看个反例:</p> <p>```jsx jsx 体验AI代码助手 代码解读复制代码const useCountStore = create((set) => ({ nested: { other: 'other', count: 0 }, inc: () => // × 这么做是错的 set((state) => ({ nested: { count: state.nested.count + 1 }, })), }))</p> <pre><code> 更新 App 组件,再看看效果。 ```jsx jsx 体验AI代码助手 代码解读复制代码function App() { const nested = useCountStore(state => state.nested) const inc = useCountStore(state => state.inc) return ( <>
{JSON.stringify(nested)}
> ) }效果:
发现嵌套对象中的 other 属性不见了。
正确的做法应该是这样:
```diff
diff 体验AI代码助手 代码解读复制代码set((state) => ({- nested: { count: state.nested.count + 1 },
- nested: { ...state.nested, count: state.nested.count + 1 },
}))
再来看看效果:
这样就没有问题了。
不过,这样更新嵌套对象的方式,着实有些麻烦,如果嵌套层级过深,写出来的代码就极其的丑陋。
jsx 体验AI代码助手 代码解读复制代码// × 不要这么做! normalInc: () => set((state) => ({ deep: { ...state.deep, nested: { ...state.deep.nested, obj: { ...state.deep.nested.obj, count: state.deep.nested.obj.count + 1 } } } })),
这个 Zustand 作者也帮我们想到了,提供了 Immer middleware 帮我们做这件事。
Immer middleware 直接依赖 immer,因此我们还需要安装 immer。
```bash
bash 体验AI代码助手 代码解读复制代码npm install immer接着,项目中引入: ```diff diff 体验AI代码助手 代码解读复制代码import { create } from 'zustand' + import { immer } from 'zustand/middleware/immer'
此 immer 非彼 immer,middleware/immer 是在 immer 之上的一层封装,为了更好地跟 Zustand 在一起协作。
还是以上方的 useCountStore() 为例。
```jsx
jsx 体验AI代码助手 代码解读复制代码const useCountStore = create((set) => ({
nested: { other: 'other', count: 0 },
inc: () =>
set((state) => ({
nested: { ...state.nested, count: state.nested.count + 1 },
})),
}))我们只做 2 件事。
- 增加按钮,点击时调用 setWho('Zustand'),将状态 who 更新为
-
create() 回调函数采用 immer 包裹
- set() 回调函数内部没有返回值,直接针对 state 进行修改
jsx 体验AI代码助手 代码解读复制代码const useCountStore = create(/* 1 */immer((set) => ({ nested: { other: 'other', count: 0 }, inc: () => set((state) => { state.nested.count++ /* 2 */ }), })))
查看效果:
跟以前一样。
一旦接入 immer,那么状态更新可以统一改成直接修改 state 的方式,这样更加一致和易于维护。
共 0 条评论关于 “React — zustand状态管理工具”