原子化状态(Jotai / Recoil)与单一 Store(Zustand / Redux)如何选型?各自解决什么渲染痛点?

在 React 现代状态管理生态中,Zustand(代表自顶向下的单一外部 Store 模型) 与 Jotai(代表自底向上的原子化 Atomic 模型) 是最主流的两套轻量方案,它们分别契合不同的业务数据结构与更新频率特征。

一、 单一 Store(Zustand)vs 原子化状态(Jotai)核心对比

评估维度 单一 Store 模型(Zustand / Redux) 原子化状态模型(Jotai / Recoil)
状态组织方式 自顶向下(Top-Down):状态集中在一个外部大对象树中,组件通过 Selector 函数切片订阅 自底向上(Bottom-Up):状态被打散为无数独立的微小 atom,通过组合派生出复杂 derivedAtom
组件外访问能力 极强:可在 Axios 拦截器、普通工具函数中直接调用 useStore.getState() 与 setState() 较弱:Atom 状态默认驻留在 React Provider 组件树内(需额外持有 store 实例才能在组件外读写)
动态高频点状更新性能 若大画布上有 5,000 个独立图元,每次更新大对象再跑 5,000 次 Selector 函数存在遍历开销 极佳:每个图元独立对应一个 atomFamily(id),修改单个图元仅唤醒订阅该 Atom 的那 1 个组件,复杂度 $O(1)$
代码分割(Code Splitting) 全局 Store 通常随主包一起加载 Atom 定义可随异步路由组件天然按需加载与销毁

二、 代码对照:同一派生业务在 Zustand 与 Jotai 中的实现差异

// 1. Zustand:外部单一 Store + 显式 Selector 订阅(适合全局用户态、购物车、播放器)
import { create } from 'zustand'

const useCartStore = create((set) => ({
  items: [{ price: 100, qty: 2 }],
  taxRate: 0.1,
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))

// 组件内必须显式写 Selector 精确选取所需字段,否则无关字段变化也会重渲染
function CartTotalZustand() {
  const total = useCartStore((s) =>
    s.items.reduce((sum, it) => sum + it.price * it.qty, 0) * (1 + s.taxRate)
  )
  return <span>总计:{total}</span>
}

// 2. Jotai:自底向上原子组合(适合可视化低代码画布、复杂联动电子表格、节点编辑器)
import { atom, useAtomValue } from 'jotai'

const itemsAtom = atom([{ price: 100, qty: 2 }])
const taxRateAtom = atom(0.1)
// 派生原子(Derived Atom):自动追踪依赖的原子,只有依赖项变化时才重算!
const totalAtom = atom((get) => {
  const items = get(itemsAtom)
  const tax = get(taxRateAtom)
  return items.reduce((sum, it) => sum + it.price * it.qty, 0) * (1 + tax)
})

function CartTotalJotai() {
  const total = useAtomValue(totalAtom)
  return <span>总计:{total}</span>
}

同分类其他题目