最近,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 的使用
基本使用
安装 Zustand:首先,使用 npm 来安装 Zustand:
npm install zustand
创建 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。
使用 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;