女王控的博客

React 全局状态库选型

背景

目前老项目使用的 redux 性能较差且难上手/代码复杂,useContext 虽然使用上简单但性能较差,需要选型一个易上手/性能好/组件化的三方库,以下是 redux/useContext 在处理复杂业务时的缺点

  1. 组件间的状态共享只能通过将 state 提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大的组件树(管理成本和子组件意外渲染问题)
  2. Context 只能存储单一值,无法存储多个各自拥有消费者的值的集合
  3. 以上两种方式都很难将组件树的顶层与子组件进行代码分割
  4. useContext 也没有提供对异步请求的解决方案,redux 虽然有但较难上手

目标

提供一种机制来管理和维护 React 应用中的状态,并且使得这些状态能够跨组件共享、状态的变化可以预测

  1. 做到数据共享,兼顾子组件精准渲染
  2. 获取和修改状态
  3. 管理异步工作流

模式

Store

Store 模式通过集中式的存储来管理应用的状态,这种模式的基本思想是将应用的所有状态存储在一个单一的地方,通常称为“store”。组件通过与 store 的交互来读取和更新状态,而不是直接在组件内部管理状态

Redux、Zustand 正是使用了 store 模式设计,Redux 官网的 gif 很好的演示了 redux 中数据流动的方向及 Store、 Dispach、Action、Reducer 的配合过程

响应

响应模式是一种通过观察(observe)状态变化并自动更新界面的方法。这种模式的核心思想是,当数据发生变化时,界面能够自动地响应并更新显示,可以使得状态管理更加直观、高效和模块化

MobX、Valtio 都使用了响应模式,MobX 官网流程图很好的解释了其 data flow

2025 10 23 10 47 41

原子

不同于 Store 模式和响应模式把 State 集中起来管理,状态维护在组件顶部,子组件需要的话通过 selector 按需获取,数据自上向下流动。原子模式提倡把应用的全局状态拆分为多个小的、独立的状态单元——这些状态单元被称为原子(Atom),提供更细粒度的状态管理,以便组件可以更高效地更新和渲染

原子模式是对 useState + useContext 的升级,原子模式的代表作是 Recoil 和 Jotai

2025 10 23 10 56 17

状态机

状态机是一种数学模型,它描述了在任何给定时间只能处于一种状态的系统,系统在不同的状态之间可以通过确定的事件进行转化

2025 10 23 10 56 28

选型

状态库 响应粒度 API 简洁度 性能 类型支持 异步处理 持久化/中间件 DevTools 社区生态 应用规模 适用场景/特点 适用具体范围 优点 缺点
Context API 粗粒度 简单 一般 完善 需手动 需手动 极成熟 小/中 全局共享、简单场景,易用
  1. 应用中全局状态较少、简单的场景;
  2. 如主题切换、登录状态、国际化等
  1. 无需额外安装库,官方内置;
  2. 简单易用,轻量级;
  3. 适合简单的全局状态共享(如主题、语言)
  1. 性能较差,任何 context 改变会导致所有消费组件重渲染;
  2. 不适合管理复杂状态
Redux 粗粒度 复杂 一般 完善 完善 完善 强(Redux DevTools) 极成熟 中/大 可预测性、可扩展性、生态强
  1. 复杂的大型应用,业务逻辑复杂,状态交互频繁;
  2. 需要时间旅行、调试工具、可预测性强的场景
  1. 社区成熟,生态丰富(如 Redux Toolkit);
  2. 可预测性强,状态集中管理;
  3. 易于调试,工具支持好(Redux DevTools);
  4. 与 React 以外的框架也可结合使用
  1. 写法繁琐(action、reducer、store 等),模板代码多;
  2. 学习曲线较高;
  3. 对于简单场景显得“重”
MobX 中等粒度 简单 完善 支持 支持 成熟 中/大 响应式、面向对象、灵活
  1. 中小型项目;
  2. 对响应式编程有需求;
  3. 不需要严格的状态可预测性
  1. 使用简单,代码量少,响应式编程体验好;
  2. 自动追踪依赖,数据变化自动更新视图;
  3. 学习成本低,易于集成
  1. 状态分散,不易全局管控;
  2. 调试和开发工具支持较 Redux 差;
  3. 难以追踪复杂的状态变更
Recoil 原子粒度 适中 完善 完善 需手动 有(Recoil DevTools) 较成熟 中/大 原子+选择器、复杂依赖关系
  1. 中大型应用;
  2. 需要细粒度状态管理和较高性能的场景
  1. 原生支持 React,API 简洁;
  2. 支持原子化状态管理,细粒度控制;
  3. 性能优秀,支持异步 selector
  1. 社区和生态相对较小;
  2. 有些高级用法不如 Redux 成熟
Zustand 任意粒度 极简 完善 需手动 完善 有(Zustand DevTools) 极成熟 小/中/大 Hook 风格、中间件、极简
  1. 中小型应用;
  2. 想要简单高效状态管理的场景;
  1. API 极简,零模板代码
  2. 性能好,支持局部订阅;
  3. 支持 hooks,易于集成
  1. 社区相对较小;
  2. 功能不如 Redux 完善,如中间件、调试工具等较弱
Jotai 原子粒度 极简 完善 支持 需手动 有(Jotai DevTools) 新兴 小/中 函数式、原子化、极简
  1. 轻量级项目;
  2. 需要响应式原子状态的场景
  1. 响应式原子状态,API 简单;
  2. 体积小,性能优秀
  1. 社区还在成长;
  2. 功能相对精简
XState 状态机/流程 复杂 完善 完善 支持 成熟 中/大 复杂业务流程、可视化 多状态流转、流程编排、复杂异步与副作用管理
  1. 可视化与状态图;
  2. 严格的状态流转;
  3. 适合复杂业务流程建模
  1. 学习曲线高;心智模型重;
  2. 在简单场景偏重
Preact Signals 极细粒度 极简 极高 需手动 需手动 有限 新兴 小/中 微秒级响应、性能极致
  1. 极致性能需求的组件局部状态;
  2. 频繁更新的 UI
  1. 超高性能;
  2. 信号机制带来精准更新;
  3. 极小 API
  1. 生态有限;
  2. DevTools 支持弱;
  3. 需手动做异步与持久化方案
Pinia 原子/模块粒度 简单 完善 支持 支持 成熟 小/中/大 Vue 原生、社区有 React 适配
  1. 跨框架(主要 Vue)或在微前端中复用;
  2. 模块化状态
  1. API 直观;
  2. 模块/插件生态好;
  3. 类型友好
  1. 在纯 React 项目里并非主流;
  2. 适配层可能增加复杂度
helux 极细粒度 丰富 极高 极好 完善 有限/需手动 新兴 小/中 响应式+按需渲染、类型友好 需要极细粒度渲染优化与强类型结合场景
  1. 按需渲染性能优秀;
  2. 类型支持出色;
  3. 组合灵活
  1. 生态新;
  2. 文档与社区资源少;
  3. 部分特性稳定性待验证

对比

代码

Context API

jsx 复制代码
import React, { createContext, useContext, useState } from 'react';

const DemoContext = createContext();

const DemoProvider = ({ children }) => {
   const [count, setCount] = useState(0);
   const [name, setName] = useState('Anonymous');

   return <DemoContext.Provider value={{ count, setCount, name, setName }}>{children}</DemoContext.Provider>;
};

const GrandSonName = () => {
   const { name } = useContext(DemoContext);
   return (
      <div style={{ border: '1px #333 solid' }}>
         <h3>GrandSonName</h3>
         <p>name: {name}</p>
      </div>
   );
};

const GrandSonCount = () => {
   const { count } = useContext(DemoContext);
   return (
      <div style={{ border: '1px #333 solid' }}>
         <h3>GrandSonCount</h3>
         <p>count: {count}</p>
      </div>
   );
};

const Son1 = () => {
   const { count, setCount, name } = useContext(DemoContext);

   return (
      <div style={{ border: '1px #333 solid', padding: 5 }}>
         <h2>Child 1</h2>
         <p>Count: {count}</p>
         <p>Name: {name}</p>
         <button onClick={() => setCount(count + 1)}>Increment Count</button>
         <GrandSonName />
         <GrandSonCount />
      </div>
   );
};

const Son2 = () => {
   const { name, setName } = useContext(DemoContext);

   return (
      <div style={{ border: '1px #333 solid', padding: 5, marginTop: 10 }}>
         <h2>Child 2</h2>
         <input type='text' value={name} onChange={(e) => setName(e.target.value)} />
         <GrandSonName />
      </div>
   );
};

const Son3 = () => {
   return (
      <div style={{ border: '1px #333 solid', padding: 5, marginTop: 10 }}>
         <h2>Child 3</h2>
         <GrandSonName />
         <GrandSonCount />
      </div>
   );
};

export default function App() {
   return (
      <DemoProvider>
         <Son1 />
         <Son2 />
         <Son3 />
      </DemoProvider>
   );
}

helux

jsx 复制代码
import { sharex, $, useAtom } from 'helux';
import React from 'react';

const GlobalModel = sharex({
   count: 1,
   name: 'Anonymous'
});

const { state, setState } = GlobalModel;

const GrandSonName = () => {
   return (
      <div style={{ border: '1px #333 solid' }}>
         <h3>GrandSonName</h3>
         <p>name: {$(state.name)}</p>
      </div>
   );
};

const GrandSonCount = () => {
   return (
      <div style={{ border: '1px #333 solid' }}>
         <h3>GrandSonCount</h3>
         <p>count: {$(state.count)}</p>
      </div>
   );
};

const Son1 = () => {
   return (
      <div style={{ border: '1px #333 solid', padding: 5 }}>
         <h2>Child 1</h2>
         <p>Count: {$(state.count)}</p>
         <p>Name: {$(state.name)}</p>
         <button
            onClick={() =>
               setState((draft) => {
                  draft.count += 1;
               })
            }
         >
            Increment Count
         </button>
         <GrandSonName />
         <GrandSonCount />
      </div>
   );
};

// 这里需要抽出一个组件,否则依然会有性能问题
const Input = () => {
   const [dictState] = useAtom(state);
   return (
      <input
         type='text'
         value={dictState.name}
         onChange={(e) => {
            setState((draft) => {
               draft.name = e.target.value;
            });
         }}
      />
   );
};

const Son2 = () => {
   return (
      <div style={{ border: '1px #333 solid', padding: 5, marginTop: 10 }}>
         <h2>Child 2</h2>
         <Input />
         <GrandSonName />
      </div>
   );
};

const Son3 = () => {
   return (
      <div style={{ border: '1px #333 solid', padding: 5, marginTop: 10 }}>
         <h2>Child 3</h2>
         <GrandSonName />
         <GrandSonCount />
      </div>
   );
};

export default function App() {
   return (
      <>
         <Son1 />
         <Son2 />
         <Son3 />
      </>
   );
}

效果

状态库 React 层重渲染 浏览器重渲染
Context API
helux

结论

以 React 层重渲染(即组件是否触发 render)为准,此标准比浏览器重渲染更高,由上面的视频可得出以下结论

  1. 虽然最终浏览器重渲染的效果一样,但 helux 对比 Context API 明显在 React 层性能更好,归功于 helux 的 dom 粒度更新

  2. helux 的 signal 语法只适用于展示型即普通的 dom 元素,无法适用于 react 组件,需要使用 useAtom 转换为真实状态,但此时就失去了 dom 粒度更新的优势,需要将 react 组件抽出一个单独组件防止性能劣化

    2025 10 23 14 52 50

快速上手

https://heluxjs.github.io/helux/

全局状态

文件夹结构

复制代码
├── model
│   ├── actions.js
│   ├── index.js
│   └── state.js

index.js

导出主入口

js 复制代码
import { $, useAtom } from 'helux';

import { GlobalState } from './state';
import * as actions from './actions'; // action 函数定义

export default GlobalState;

export const action = GlobalState.defineActions()(actions);

const { state, setState } = GlobalState;

export { $, useAtom, state, setState };

state.js

全局状态定义

js 复制代码
import { sharex } from 'helux';

export const GlobalState = sharex(
   {
      key: null,
      key2: null,
      jobInfo: {}
   },
   { moduleName: 'JobDetailState' }
);

actions.js

异步 action 定义

js 复制代码
import { getJobInfo } from '../services';

export async function queryJobInfo({ draft, payload }) {
   const [error, res] = await getJobInfo({ id: payload.jobId });
   if (error) {
      return null;
   }
   draft.jobInfo = res;
   return res;
}

纯展示类型

jsx 复制代码
import { $, state, setState } from './model';

// 纯展示型即 dom 结点直接使用 signal 语法,即 $(state.key)
export default function Example() {
   return (
      <div>
         <Button
            onClick={() =>
               setState((draft) => {
                  draft.key += 1;
               })
            }
         >
            {$(state.key)}
         </Button>
      </div>
   );
}

React 状态类型

jsx 复制代码
import { $, state, setState, useAtom } from './model';

// 这里需要抽出一个组件隔离渲染,即 useAtom 会触发组件重渲染
const Input = () => {
   const [dictState] = useAtom(state); // 由于下面使用了 key2,更新 key2 时才会触发重渲染,即更新 key 时不会触发重渲染
   return (
      <input
         type='text'
         value={dictState.key2}
         onChange={(e) => {
            setState((draft) => {
               draft.key2 = e.target.value;
            });
         }}
      />
   );
};

// React 组件由于需要取到原始值,需要使用 useAtom 包一下取出
export default function Example2() {
   return (
      <div>
         <Input />
         {$(state.key2)}
      </div>
   );
}

异步请求类型

这里将请求放到全局状态里面,而不是组件里面,推荐在全局状态和异步请求有关时,优先在这里去请求,理由如下

  1. 为了避免组件的重渲染,即如果请求在顶级组件的话,那么所有组件都会重渲染

  2. 写成 action 也有利于调试,安装 Redux DevTools 插件

    2025 10 23 16 21 40

    2025 10 23 16 22 01

jsx 复制代码
import { $, state, setState, action } from './model';

const JobDetail = ({ jobId }) => {
   // const { queryJobInfo } = action.useLoading();
   // console.log('useLoading', queryJobInfo) // 请求的状态

   const handleEvent = () => {
      console.log('state.jobId', state.jobInfo); // 事件触发的可以直接通过 state 拿到最新值
   };

   return (
      <div className={styles.container}>
         <Button
            onClick={() =>
               setState(async (draft) => {
                  const data = await action.eActions.queryJobInfo({ jobId });
                  console.log('data', data);
               })
            }
         >
            {$(state.failedFNGPMetrics.total)} // 用 $ 或者 useAtom 取到
         </Button>
      </div>
   );
};

export default JobDetail;

参考链接

  1. 全都要!一文带你全面体验八种状态管理库
  2. 你踩过 react 生态的 signal 坑吗?且看 helux 如何应对
  3. Redux、Zustand、Mobx、Valtio、Recoil、jotai、XState 状态管理怎么选

评论

阅读上一篇

数据库连接工具 web 版
2025-10-23 16:40:42

阅读下一篇

前后端调试工具应用
2025-04-17 18:49:44
0%