Skip to content

你是否了解像 Zustand、Jotai 这类新兴的状态管理库?它们各自有什么特点?

🎨Zustand 和 Jotai 都是 React 生态中备受关注的新兴状态管理库,它们都由 Poimandres(一个专注于创造高质量 React 工具的组织)的开发者们创建或维护,旨在提供比传统 Redux 或 ⁠useContext + ⁠useReducer 更简洁、更现代的状态管理方案。 有趣的是,尽管它们“师出同门”,但解决问题的哲学和路径却截然不同。我们可以将 Zustand 理解为一种“自上而下”的全局状态管理,而 Jotai 则是一种“自下而上”的原子化状态管理。 下面,我们来分别剖析它们的特点和适用场景。

Zustand:轻量、灵活的全局状态“便利店”

Zustand 的核心设计思想,可以看作是 Redux 思想的现代化、轻量化实现。它借鉴了 Flux 架构中“单一数据源”和“不可变更新”的理念,但去除了大量的模板代码和繁琐的“仪式感”。

它是如何工作的?

Zustand 的使用非常直观。我们通过一个 create 函数来创建一个“Store”,这个 Store 本质上是一个包含了状态和更新动作的 Hook。

javascript

import { create } from 'zustand';

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

// 2. 在任何组件中使用
function BearCounter() {
  // 通过 Selector 读取状态,只有当 `bears` 值变化时,组件才会重新渲染
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} around here ...</h1>;
}

function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation);
  return <button onClick={increasePopulation}>one up</button>;
}

核心特点:

  1. 极简 API 与零模板代码:没有 ActionsReducersDispatch 等概念,所有状态和逻辑都封装在 create 函数中,上手成本极低。

  2. 无需 Provider:Zustand 的状态是全局的,不依赖于 React 的 Context 机制。这意味着我们不必在应用的根部用 <Provider> 包裹整个应用,可以随时在任何组件中直接调用。

  3. 基于 Selector 的性能优化:这是 Zustand 的一个关键优势。组件通过 selector 函数(例如 state => state.bears)来订阅状态的一部分。只有当这部分被订阅的状态发生变化时,组件才会触发重渲染,从而天然地避免了 useContext 在状态对象部分更新时导致所有消费者组件重渲染的问题。

  4. 灵活性高:支持中间件(Middleware),可以轻松集成 Redux DevTools、日志、持久化存储(persist)等功能。同时,它不与 React 强绑定,其核心逻辑也可以在非 React 环境中使用。

适用场景:

  • 需要一个比 useContext 性能更好、但比 Redux 轻量得多的全局状态解决方案。

  • 管理那些本质上就是全局共享的状态,如:用户认证信息、主题(深/浅色模式)、国际化语言配置等。

  • 从 Redux 项目迁移,希望保留 Flux 思想但简化代码。

Jotai:自下而上的原子化状态“乐高积木”

Jotai 的灵感来源于 Recoil,它采用了“原子化(Atomic)”的状态管理模型。在这种模型中,状态不再是一个庞大的单一对象,而是被拆分成许多微小的、独立的状态单元,称为“Atom”。

它是如何工作的?

Jotai 的 API 设计巧妙地模仿了 React 的 useState,使得开发者可以无缝切换。

javascript
import { atom, useAtom } from 'jotai';

// 1. 创建一个 Atom,它是一个独立的状态单元
const countAtom = atom(0);

// 我们可以创建“派生 Atom”,它的值依赖于其他 Atom
const doubleCountAtom = atom((get) => get(countAtom) * 2);

// 2. 在组件中使用,API 酷似 useState
function Counter() {
  // useAtom 返回的值和 useState 一样:[value, setValue]
  const [count, setCount] = useAtom(countAtom);
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount((c) => c + 1)}>one up</button>
    </div>
  );
}

function DoubleCounter() {
    // 读取派生 Atom 的值
    const [doubleCount] = useAtom(doubleCountAtom);
    return <p>Double: {doubleCount}</p>
}

核心特点:

  1. 原子化与自下而上:状态被组织成细粒度的 atom。这种“自下而上”的构建方式非常适合将组件内部的状态提升为可共享的全局状态,符合 React 的组件化思维。

  2. 类似 useState 的心智模型**:useAtom 的 API 设计与 useState 几乎完全一致,极大地降低了学习和使用的门槛。

  3. 精准的依赖追踪与自动优化:Jotai 会自动构建一个 Atom 之间的依赖关系图。当一个 Atom 更新时,只有精确依赖于它的其他 Atom 或使用它的组件才会重新渲染。这种优化是自动完成的,开发者无需像 Zustand 那样手动编写 selector。

  4. 强大的派生状态(Derived State):可以轻松地通过组合其他 Atom 来创建新的派生 Atom,用于处理复杂的计算或异步数据流,代码表达力非常强。

适用场景:

  • UI 中存在大量离散、动态、相互关联的状态,例如复杂的表单、图形编辑器、仪表盘等。

  • 希望状态管理的模式能与 React 组件的构建模式保持高度一致。

  • 当一个状态的更新逻辑需要触发一系列精确、链式的UI变化时。

对比总结:如何选择?

为了更直观地对比,我们可以用一个表格来总结:

特性维度ZustandJotai
核心理念自上而下的全局 Store自下而上的原子化状态
API 设计create 创建单一 Store,类似 Reduxatom 创建状态单元,useAtom 类似 useState
Provider无需 <Provider>需要 <Provider> (尽管简单场景可省略)
性能优化手动:通过 Selector 订阅状态切片自动:通过 Atom 依赖图进行精准更新
心智模型更接近 Flux/Redux更接近 React useState / Recoil
适用场景管理明确的全局状态,作为轻量版 Redux管理分散、组合、相互依赖的动态状态

结论:

  • 如果我们的需求是**“我需要一个简单好用的全局状态管理器来替代 Redux 或 useContext”**,那么 Zustand 通常是更直接、更快速的选择。它像一个功能齐全的便利店,能快速满足我们对全局状态的大多数需求。

  • 如果我们的应用是由大量小型、独立且可能相互依赖的组件构成,并且我们思考问题的方式是**“如何让这个组件的 useState 能被其他组件共享?”**,那么 Jotai 会提供一个更优雅、更符合 React 直觉的解决方案。它像一套乐高积木,让我们可以灵活地组合出任何复杂的状态结构。

两者都是非常出色的库,没有绝对的优劣之分。理解它们各自的设计哲学,并根据项目需求和团队习惯做出选择,才是最关键的。

不知道说啥了很无语了