最近,GitHub 上拥有 20k Star 的 React 状态管理库 Recoil 正式停止维护,其 GitHub 仓库已被归档。Recoil 由 Meta 公司开源,然而,值得注意的是,Meta 已解雇 Recoil 团队的所有成员,且该库已有接近两年的时间未进行更新,因此其停止维护似乎已成定局。
在 2025 年的当下,提到 React 状态管理,我依旧首推 Zustand,它比 Redux、Mobx 的思维模型更简单,没有那么复杂的样板代码要写。下面就来简单看看 Zustand 的用法。
Zustand 是什么?
Zustand 是一个小型、快速且可扩展的状态管理解决方案,基于简化的 Flux 原则,使用基于 Hooks 的 API,不包含样板代码且不具有强制性。Zustand 在 Github 拥有近 50k Star,其 npm 每周下载量近 500w。
Zustand 的特点如下:
-
简单性:与 Redux 相比,Zustand 更简单且不具有强制性,不需要像 React-Redux 那样使用 Context Provider 包裹应用。
-
基于 Hooks:提供了直观易用的 Hooks 接口,让开发者可以轻松地与状态进行交互,减少样板代码。
-
单一数据源:整个应用的状态被集中存储在一个 store 中,该 store 可以被分割成多个状态切片,每个切片负责一部分应用逻辑。
-
不可变性:状态更新是不可变的,更新状态时需要创建一个新的状态对象,而不是直接修改现有状态,从而简化状态管理并防止常见的可变性相关错误。
-
订阅和选择性响应性:组件可以订阅特定的状态切片,并在这些切片发生变化时自动重新渲染。
-
细粒度依赖跟踪:使用代理实现对状态变化的细粒度跟踪,确保只有当相关状态发生变更时才会触发组件的重新渲染,最大限度减少了不必要的更新。
Zustand 的使用
基本使用
1.安装 Zustand:首先,使用 npm 来安装 Zustand:
npm install zustand
2.创建 Store:在 Zustand 中,Store是通过create
函数创建的。每个Store都包含状态和处理状态的函数。
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0, // 初始状态
increment: () => set((state) => ({ count: state.count + 1 })), // 增加count的函数
decrement: () => set((state) => ({ count: state.count - 1 })), // 减少count的函数
}));
create
函数接受一个回调函数,该回调函数接受一个set
函数作为参数,用于更新状态。在这个回调函数中,定义了一个count
状态和两个更新函数increment
和decrement
。
3.使用 Store:在组件中,可以使用自定义的 Hooks(上面的useStore
)来获取状态和更新函数,并在组件中使用它们。
import React from 'react';
import { useStore } from './store';
function Counter() {
const { count, increment, decrement } = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
下面是一个较复杂的状态管理:
import create from 'zustand';
const useUserStore = create((set) => ({
user: {
name: '',
age: 0,
email: ''
},
setUserName: (newName) => set((state) => ({ user: {...state.user, name: newName } })),
setUserAge: (newAge) => set((state) => ({ user: {...state.user, age: newAge } })),
setUserEmail: (newEmail) => set((state) => ({ user: {...state.user, email: newEmail } }))
}));
订阅特定状态片段
在 Zustand 中,如果有一个包含多个状态的store,但在组件中只需要订阅其中一个状态,可以通过解构赋值从useStore
返回的完整状态对象中提取需要的状态。Zustand的智能选择器功能允许这样做,而不会导致不必要的重新渲染。下面来看个简单的例子。
在这个store中,有两个状态:count
和name
,以及两个更新这些状态的函数。
// store.js
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
name: 'Zustand Store',
increment: () => set((state) => ({ count: state.count + 1 })),
setName: (newName) => set({ name: newName }),
}));
export default useStore;
现在,在组件中,如果只想订阅count
状态,可以这样做:
// MyComponent.js
import React from 'react';
import useStore from './store';
function MyComponent() {
// 使用解构赋值从store状态中提取count
const { count } = useStore((state) => ({ count: state.count }));
return (
<div>
<p>Count: {count}</p>
</div>
);
}
export default MyComponent;
在组件中,传递了一个选择器函数给useStore
。这个选择器函数接受当前的store
状态作为参数,并返回需要的部分状态(在这个例子中是count
)。这样,Zustand就知道只需要在count
状态变化时通知这个组件。
如果想要订阅多个状态,但不想订阅全部状态,可以在选择器函数中返回多个状态:
const { count, name } = useStore((state) => ({ count: state.count, name: state.name }));
使用中间件
Zustand 支持中间件,可以通过中间件来扩展其功能。例如,可以使用内置的persist
中间件将状态保存到本地存储,或者使用devtools
中间件在浏览器的 Redux DevTools 扩展中查看和调试 Zustand store 的状态变化等。
在下面的例子中,使用persist
中间件将count
状态保存到浏览器的 localStorage 中:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCounterStore = create(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-storage', // 本地存储的key
}
)
);
我们还可以根据需求来自定义中间件,格式如下:
const myCustomMiddleware = (config) => (set, get, api) => {
// 预处理逻辑
const result = config(set, get, api); // 调用原始配置
// 后处理逻辑
return result;
};
然后,可以像这样来应用自定义中间件:
import { create } from 'zustand';
const createStore = (set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
});
const useStore = create(myCustomMiddleware(createStore));
export default useStore;
如果想同时应用多个中间件,可以直接将它们链式调用:
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
const useStore = create(
devtools(persist(createStore, { name: 'counter-storage' }), { name: 'my-counter-store' })
);
export default useStore;
异步支持
Zustand 默认支持异步操作,最直接的方式是在创建 store
的时候定义异步函数。可以像定义同步动作一样定义异步动作,只需确保它们返回 Promise。
import { create } from 'zustand';
const useStore = create((set) => ({
data: null,
loading: false,
error: null,
fetchData: async () => {
set({ loading: true, error: null }); // 设置加载状态
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
set({ data, loading: false }); // 更新状态为成功
} catch (err) {
set({ error: err.message, loading: false }); // 更新状态为失败
}
},
}));
export default useStore;