Nanshan Blog
首页项目归档照片墙音乐说说杂谈友链关于
封面

状态管理 zustand 学习

写作时间:2026-03-28 07:00:00
# 学术
# React
# Zustand 状态管理学习指南

## 1. 什么是 Zustand?
Zustand(德语“状态”的意思)是一个基于 Hooks 的轻量级、极简的 React 状态管理库。它的核心优势包括:
- **轻量极简**:gzip 压缩后仅约 1KB,学习成本极低。
- **无 Provider 包裹**:不需要像 Redux 或 Context 那样在根组件嵌套 Provider,使用更加灵活。
- **按需订阅**:通过 Selector(选择器)机制,精准订阅所需状态,避免不必要的组件重渲染。
- **TypeScript 友好**:提供完善的类型推导。
- **强大的中间件生态**:内置支持 `persist`(持久化)、`devtools`(调试)、`immer`(不可变数据)等。

## 2. 快速上手

### 2.1 安装
```bash
npm install zustand
# 或者
yarn add zustand

2.2 创建 Store

使用 create 函数创建一个 Store。Store 本质上是一个自定义 Hook,里面可以存放基础变量、对象、函数等任意内容。

import { create } from 'zustand';

const useBearStore = create((set) => ({
  bears: 0,
  // 依赖旧状态更新时,推荐使用函数式更新(避免闭包陷阱)
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  // 直接赋值(无需依赖旧状态)
  removeAllBears: () => set({ bears: 0 }),
}));

2.3 在组件中使用

在组件中调用 Store Hook,并通过 Selector 提取所需状态:

function BearCounter() {
  // 仅订阅 bears,只有 bears 变化时组件才会重渲染
  const bears = useBearStore((state) => state.bears);
  return ;
}

function Controls() {
  // 仅订阅 action 函数
  const increasePopulation = useBearStore((state) => state.increasePopulation);
  return ;
}

3. 核心进阶用法

3.1 异步状态管理

Zustand 原生支持异步操作,无需引入额外的中间件。直接在 action 中编写异步逻辑,并在请求完成后调用 set 更新状态即可。

const useUserStore = create((set) => ({
  user: null,
  loading: false,
  fetchUser: async (userId) => {
    set({ loading: true });
    try {
      const res = await fetch(`/api/user/${userId}`);
      const data = await res.json();
      set({ user: data, loading: false });
    } catch (error) {
      console.error(error);
      set({ loading: false });
    }
  },
}));

3.2 嵌套状态与 Immer 集成

Zustand 的 set 方法默认只进行浅合并(Shallow Merge)。如果状态是深度嵌套的对象,手动使用展开运算符(...)会非常繁琐。此时可以结合 immer 中间件,像修改普通对象一样修改状态:

import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

const useDeepStore = create(
  immer((set) => ({
    deep: { nested: { obj: { count: 0 } } },
    inc: () =>
      set((state) => {
        // 直接修改,Immer 会自动处理不可变更新
        state.deep.nested.obj.count += 1;
      }),
  }))
);

4. 性能优化最佳实践

4.1 精准订阅(避免全量重渲染)

错误写法:直接解构整个 Store,会导致任意状态变化时组件都会重渲染。

const { bears, increasePopulation } = useBearStore(); // 不推荐

🟢 正确写法:使用 Selector 提取具体值。

const bears = useBearStore((state) => state.bears);

4.2 订阅多个状态与浅比较

当需要同时订阅多个状态时,如果直接返回一个对象,由于每次渲染都会生成新的对象引用,会导致无限重渲染。此时需要引入 shallow 比较函数:

import { shallow } from 'zustand/shallow';

const { bears, user } = useBearStore(
  (state) => ({ bears: state.bears, user: state.user }),
  shallow // 浅比较:对比对象内属性的值,而非引用地址
);

4.3 按业务模块拆分 Store

当应用规模变大时,官方推荐将 Store 按业务模块拆分,而不是维护一个巨大的全局 Store:

// store/userStore.js
export const useUserStore = create((set) => ({ /* 用户状态 */ }));

// store/cartStore.js
export const useCartStore = create((set) => ({ /* 购物车状态 */ }));

5. 实用中间件与调试

5.1 状态持久化 (Persist)

将状态自动同步到 localStorage 或 sessionStorage,刷新页面后状态不丢失:

import { persist } from 'zustand/middleware';

const useThemeStore = create(
  persist(
    (set) => ({
      theme: 'light',
      toggleTheme: () => set((state) => ({ 
        theme: state.theme === 'light' ? 'dark' : 'light' 
      })),
    }),
    { name: 'theme-storage' } // 本地存储的 key
  )
);

5.2 接入 Redux DevTools

借助 devtools 中间件,可以在浏览器控制台的 Redux DevTools 面板中查看状态变更历史、时间旅行调试:

import { devtools } from 'zustand/middleware';

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

6. 官方强调的注意事项

  1. 依赖旧状态必须用函数参数:更新状态时,如果新值依赖旧值,务必使用 set((state) => ...) 的函数形式,防止因闭包问题获取到过期的旧状态。
  2. 批量更新:Zustand 内部会自动对多次连续的 set 进行批量合并处理,无需手动使用 unstable_batchedUpdates,有效避免了多次重渲染。
  3. 异步请求与组件卸载:在组件卸载时,如果异步请求尚未完成,直接调用 set 可能会触发 React 警告。建议在 useEffect 的清理函数中使用 AbortController 取消未完成的请求。
avatar

NanShan

白天在代码里修Bug,晚上在赛博空间里修补自己——一个用义体升级的程序员

RECOMMENDED

Vue Router 介绍

2026-03-26 07:00:00

Table of Contents