人人都会AI编程

13.3 轻量级状态库

更新时间:2026-07-11

当应用状态复杂度超过 useState + useContext 的承载能力,但又不至于引入 Redux 这类重型方案时,轻量级状态库是极佳的选择。它们通常体积小、API 简洁、学习成本低,同时提供了细粒度更新、状态派生、异步处理等实用能力。

13.3.1 Zustand:极简 API、无 Provider 的状态管理

Zustand(德语“状态”)是一个基于 Hook 的轻量状态管理库,核心理念是 “状态就是一份普通的 JavaScript 对象,你可以直接修改它”。它不需要 Provider 包裹,也不需要 dispatch action,用起来几乎零模板代码。

基本用法:

npm install zustand
import { create } from 'zustand';

// 定义一个 store
const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

在组件中使用:

function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} 只熊</h1>;
}

function Controls() {
  const increase = useBearStore((state) => state.increasePopulation);
  return <button onClick={increase}>增加一只</button>;
}

Zustand 的 set 方法会合并状态(类似 setState),可以直接传入新对象,也可以传入一个基于旧状态的函数。选择器(selector)机制确保了组件的精确更新——只有选中的状态变化时,组件才会重新渲染。

处理异步操作:

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

异步逻辑就写在 store 的创建函数中,和同步操作一样自然,不需要额外的中间件(当然 Zustand 也支持中间件,例如 persist 持久化)。

核心优势:

  • 体积极小:gzip 后仅约 1KB。
  • 无 Provider:直接 import store 的 hook 即可在任意组件中使用,组件树无需包裹。
  • 选择器更新:默认使用 Object.is 比较,可自定义 shallow 比较器以避免不必要渲染。
  • 灵活的结构:可以将多个相关状态集中在一个 store,也可以拆分多个小 store,完全自由。

适用场景: 中小型应用、需要跨组件共享状态的场景,特别适合追求极简架构的开发者。

13.3.2 Jotai:原子化状态管理

Jotai(日语“状态”)受 Recoil 启发,采用原子化(Atom) 的状态管理模式。状态被拆分成一个个独立的原子,每个原子可以被任意组件读取和写入,原子之间可以相互派生,更新只会影响依赖它的原子和组件,实现极度细粒度的重渲染控制。

基本用法:

npm install jotai
import { atom, useAtom } from 'jotai';

// 定义一个原子
const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      {count}
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  );
}

派生原子(Derived Atoms):

const doubledAtom = atom((get) => get(countAtom) * 2);

function DoubledDisplay() {
  const [doubled] = useAtom(doubledAtom);
  return <div>{doubled}</div>;
}

派生原子是只读的,它的值随着依赖的原始原子自动更新。

异步原子:

const userAtom = atom(async () => {
  const res = await fetch('/api/user');
  return res.json();
});

function User() {
  const [user] = useAtom(userAtom);
  // 初次渲染时 user 是 undefined,可用 Suspense 或自行处理加载态
  return <div>{user?.name}</div>;
}

Jotai 支持 Suspense,可以在异步原子就绪前显示 fallback。

核心优势:

  • 极致细粒度更新:状态拆分到原子级别,只有依赖某个原子的组件才会重渲染。
  • 组合灵活:原子可以自由组合派生新的原子,构建复杂数据流仍保持清晰。
  • Provider 可选:默认使用全局 store,也可以手动提供 Provider 实现局部隔离或测试。
  • Suspense 集成:原生支持异步原子与 Suspense。

适用场景: 需要极高渲染性能的大型应用,或者喜欢函数式、原子化设计思想的状态管理场景。

13.3.3 Recoil:原子化状态与派生状态

Recoil 同样是 Facebook 开发的原子化状态管理库,提供了 atomselector 概念。不过需注意,Recoil 当前仍属于实验性项目,版本更新较慢,社区活跃度不如 Zustand 和 Jotai。此处仅做简要介绍。

基本用法:

import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';

const textState = atom({
  key: 'textState',
  default: '',
});

const charCountState = selector({
  key: 'charCountState',
  get: ({get}) => {
    const text = get(textState);
    return text.length;
  },
});

function TextInput() {
  const [text, setText] = useRecoilState(textState);
  return <input value={text} onChange={(e) => setText(e.target.value)} />;
}

function CharCount() {
  const count = useRecoilValue(charCountState);
  return <div>字符数: {count}</div>;
}

Recoil 必须使用 RecoilRoot 包裹整个应用,每个 atom/selector 需要一个全局唯一的 key,这在大型应用中可能显得有些啰嗦。

核心特点:

  • 原生 React 思维:API 与 useState 非常相似。
  • 数据流图(Data Flow Graph):状态之间的关系是可追踪、可调试的。
  • 并发特性:支持 React 18 并发模式。

但鉴于其实验性质和相对较高的复杂度,新项目通常更推荐 Zustand 或 Jotai(Jotai 本身就是 Recoil 的简化增强版)。

13.3.4 轻量级状态库对比与选型

| 特性 | Zustand | Jotai | Recoil |
|------|---------|-------|--------|
| 设计理念 | 单一 store(可分拆) | 原子化状态 | 原子化状态 |
| 是否需要 Provider | 否 | 否(可选) | 是(RecoilRoot) |
| 学习曲线 | 极低 | 中等 | 中等 |
| 体积 | ~1KB | ~3KB | 较大 |
| 细粒度更新 | 通过选择器 | 自动按原子依赖 | 自动按原子/选择器依赖 |
| 异步支持 | 原生写法 | 异步原子 + Suspense | 异步选择器 + Suspense |
| 社区活跃度 | 高 | 高 | 较低(实验性) |

选型建议

  • 追求极简:选 Zustand。写起来和 useState 一样自然,零样板代码,体积忽略不计,是快速开发的利器。
  • 需要极致渲染性能:选 Jotai。原子化让状态依赖更清晰,避免大范围重渲染,适合复杂交互和大型列表。
  • Recoil 实验项目或已有技术栈:如果团队已经使用 Recoil 且稳定,可以继续;新项目如果不需其特定功能,建议优先考虑 Zustand 或 Jotai。
  • 混合使用:这三个库并不互斥,你可以同时使用 Zustand 管理全局业务状态,用 Jotai 管理某些局部但需要共享的 UI 状态,完全可行。

轻量级状态库的核心价值在于“刚刚好”——提供足够的结构来组织状态,又不强加过多的概念和规则,让开发者保持高效的开发节奏。