跳到主要内容

核心

Zustand 是一个轻量级、灵活强大的 React 状态管理库,采用 hooks API 设计。它提供简洁的 API 和出色的性能,是中小型项目的理想选择。

安装
npm install zustand
# yarn add zustand
# pnpm add zustand

一、创建 Store

使用 create 函数定义全局状态容器。

store.js
import create from 'zustand';

// 基础用法
const useStore = create(set => ({
count: 0,
increment: () => set({ count: state => state.count + 1 }),
decrement: () => set({ count: state => state.count - 1 }),
reset: () => set({ count: 0 }),
}));

// 带中间件的用法(见下文)
export default useStore;
  • set 函数:更新状态,支持直接赋值或函数式更新
  • 状态可以是任意类型(数字、字符串、对象等)
  • 修改方法通过 set 更新状态,支持函数式更新(依赖当前状态)和直接赋值
  • 返回值:返回包含状态和操作的对象

二、组件中订阅状态

1. 选择器(Selector) - 推荐方式

在组件中选择性订阅状态片段,避免不必要的重渲染。

import useStore from './store';

function Counter() {
// 只订阅 count 状态
const count = userStore(state => state.count);

// 订阅多个状态
const { increment, decrement } = useStore(state => ({
increment: state.increment,
decrement: state.decrement,
}));

return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
);
}
  • 选择器函数(state) => value 指定订阅的数据
  • 自动优化:仅当选择的数据变化时组件重渲染
  • 性能对比
    • 订阅整个状态时,任何状态变更都会触发组件重渲染
    • 使用选择器时,仅当订阅的字段变更时触发重渲染

2. 使用 shallow 比较

避免因对象引用变化导致的重新渲染:

import { shallow } from 'zustand/shallow';

// 仅当 items 或 loading 变化时重新渲染
const { items, loading } = useCounterStore(
state => ({
items: state.items,
loading: state.loading,
}),
shallow,
);

3. 订阅单个状态(使用 subscribeWithSelector

避免订阅整个 store 的变化,只关注特定状态:

import { create, subScribeWithSelector } from 'zustand';

// 创建带订阅功能的 Store
const useAgeStore = create(
subScribeWithSelector(set => ({
age: 0,
name: '张三',
})),
);

// 仅订阅 age 状态变化
const [status, setStatus] = useState('单身');

useAgeStore.subscribe(
state => state.age,
(age, preAge) => {
if (age >= 26) {
setStatus('结婚');
} else {
setStatus('单身');
}
},
);

三、 使用中间件

中间件增强 store 功能(日志、持久化等)

1. devtools (调试工具)

需要安装浏览器插件 Redux Devtools

import { devtools } from 'zustand/middleware';

const useUserStore = create(
devtools(
set => ({
name: 'Tom',
age: 86,
hobby: {
sing: 'All in How Much We Give',
dance: 'Jazz dance',
},
setHobbyRap: rap =>
set(state => {
state.bobby.sing = rap;
}),
}),
{
enabled: true,
name: '用户信息',
},
),
);

2. persist (持久化储存)

将状态保存到 localStoragesessionStorage 等。

import create from 'zustand';
import { devtools, persist } from 'zustand/middleware';

const useStore = create(
persist(
set => ({
name: 'Tom',
age: 86,
hobby: {
sing: 'All in How Much We Give',
dance: 'Jazz dance',
},
serUser: user => set({ user }),
}),
{
name: 'user-storage', // localStorage 键名
getStorage: createJsonStorage(() => sessionStorage), // 改用 sessionStorage
partialize: state => ({
name: state.name,
age: state.age,
hobby: state.bobby,
}),
},
),
);
  • persist: 状态持久化 localStorage/sessionStorage

3. immer (简化不可变更新)

import { immer } from 'zustand/middleware';

const useUserStore = create(
immer(set => ({
name: 'Tom',
age: 86,
hobby: {
sing: 'All in How Much We Give',
dance: 'Jazz dance',
},
setHobbyRap: sing =>
set(state => {
state.hobby.sing = sing;
}),
})),
);

4. 自定义中间件

const loggerMiddleware = config => (set, get, api) => {
return config(
args => {
console.log('Dispatching', args);
set(args);
},
get,
api,
);
};

const useStore = create(
loggerMiddleware(set => ({
todos: [],
addTodo: text =>
set(state => ({
todos: [...state.todos, { text, id: Date.now() }],
})),
})),
);

5. 组合中间件

import { combine } from 'zustand/middleware';

const useStore = create(
combine({ count: 0 }, set => ({
increment: () =>
set(state => ({
count: state.count + 1,
})),
})),
devtools(),
persist(),
);

四、 高级用法

1. 异步操作

const useStore = create(set => ({
data: null,
loading: false,
fetchData: async () => {
set({ loading: true });
try {
const res = await fetch('/api/data');
const data = await res.json();

set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));

2. 派生状态

const useStore = create(set => ({
items: [],
addItem: item =>
set(state => ({
items: [...state.item, item],
})),
// 派生状态不需要在 store 中定义
get itemCount() {
return this.items.length;
},
}));

// 在组件中使用
const count = useStore(state => state.itemCount);

3. 拆分 Store

避免单个大 Store 导致的性能问题:

// 用户状态
const useUserStore = create(/** */);

// 购物车状态
const useCartStore = create(/** */);

五、使用总结

特性说明
Provider不需要包裹组件树
精准订阅组件只订阅选择的数据片段
轻量级~3kb gipped
中间件生态支持日志、持久化、中间件组合等
TypeScript 友好自动推断类型,支持泛型定义