jotai、mobx-react-lite、react-query、recoil、redux 和 zustand 都是 React 生态中用于管理应用状态或处理异步数据的主流库,但它们解决的问题、设计哲学和适用场景各有不同。redux 是最经典的状态容器,强调可预测性和中间件生态;zustand 和 jotai 提供更轻量、简洁的原子化状态模型;recoil 由 Meta 推出,以图状依赖和派生状态为核心;mobx-react-lite 基于响应式编程,通过可观察对象自动追踪依赖;而 react-query(现为 TanStack Query)专注于服务端状态管理,如缓存、同步和后台更新,不直接处理 UI 状态。这些库可以单独使用,也可根据需求组合(如用 zustand 管理 UI 状态 + react-query 管理服务端数据)。
在现代 React 开发中,状态管理早已不是“是否需要”的问题,而是“如何选型”。本文将深入对比 jotai、mobx-react-lite、react-query、recoil、redux 和 zustand 六大主流方案,从核心理念、代码风格、适用边界到真实工程权衡,帮助你做出精准技术决策。
首先必须明确:并非所有库都解决同一类问题。
jotai、mobx-react-lite、recoil、redux、zustand 主要用于管理客户端 UI 状态(如表单输入、模态框开关、主题设置等)。react-query 专为服务端数据(如 API 响应)设计,提供缓存、同步、失效等机制,不应用于存储 UI 状态。💡 实践建议:多数应用应同时使用两类工具 —— 例如用
zustand管理 UI 状态,用react-query管理用户资料、文章列表等服务端数据。
下面我们将聚焦于 UI 状态管理库的对比(react-query 会在专门章节说明)。
jotai:原子化状态(Atom-based)jotai 将状态拆分为独立的“原子”(atoms),组件直接读写原子,依赖自动追踪。
// jotai
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);
function Counter() {
const [count, setCount] = useAtom(countAtom);
const double = useAtomValue(doubleCountAtom);
return <div>{count} → {double}</div>;
}
zustand:单一 Store + Hookszustand 使用单一 store,但通过 Hooks 暴露部分状态,避免全量重渲染。
// zustand
import { create } from 'zustand';
const useStore = create((set, get) => ({
count: 0,
double: () => get().count * 2,
inc: () => set((state) => ({ count: state.count + 1 }))
}));
function Counter() {
const { count, double, inc } = useStore();
return <div>{count} → {double()}</div>;
}
recoil:原子 + 选择器(Selectors)recoil 的原子(atom)类似 jotai,但派生状态通过 selector 显式定义。
// recoil
import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';
const countAtom = atom({ key: 'count', default: 0 });
const doubleCountSelector = selector({
key: 'doubleCount',
get: ({ get }) => get(countAtom) * 2
});
function Counter() {
const [count, setCount] = useRecoilState(countAtom);
const double = useRecoilValue(doubleCountSelector);
return <div>{count} → {double}</div>;
}
redux:不可变全局 Storeredux 要求通过 reducer 函数更新不可变状态,通常配合 Redux Toolkit 简化。
// redux + RTK
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { useSelector, useDispatch } from 'react-redux';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => { state.count += 1; }
}
});
const store = configureStore({ reducer: counterSlice.reducer });
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return <div>{count} → {count * 2}</div>;
}
mobx-react-lite:响应式对象mobx-react-lite 将状态定义为可观察对象,修改属性自动触发重渲染。
// mobx-react-lite
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
class CounterStore {
count = 0;
constructor() { makeAutoObservable(this); }
get double() { return this.count * 2; }
increment = () => { this.count++; };
}
const store = new CounterStore();
const Counter = observer(() => (
<div>{store.count} → {store.double}</div>
));
所有库都致力于避免不必要的重渲染,但实现方式不同:
jotai / recoil:基于原子依赖,仅当所用原子变化时重渲染。zustand:通过 useStore(selector) 选择部分状态,类似 useSelector。redux:依赖 useSelector 的浅比较或自定义比较函数。mobx-react-lite:自动追踪组件内访问的可观察属性,仅当这些属性变化时重渲染。// zustand: 选择部分状态避免重渲染
const count = useStore(state => state.count); // 只有 count 变化时重渲染
// redux: 同样需选择
const count = useSelector(state => state.count);
react-query:服务端状态的黄金标准react-query 不管理 UI 状态,而是处理 API 数据的获取、缓存和同步:
// react-query
import { useQuery, useMutation } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId)
});
const mutation = useMutation({
mutationFn: updateUser,
onSuccess: () => queryClient.invalidateQueries(['user', userId])
});
if (isLoading) return <div>Loading...</div>;
return <div>{data.name}</div>;
}
其他 UI 状态库通常不内置异步逻辑,需自行集成:
redux:通过 middleware(如 Redux Thunk)zustand / jotai:在 action 中直接使用 async/awaitmobx:在 action 中处理异步// zustand with async
const useStore = create((set) => ({
user: null,
fetchUser: async (id) => {
const user = await api.getUser(id);
set({ user });
}
}));
redux:拥有最完善的 DevTools(时间旅行、action 日志),但需额外配置。mobx-react-lite:DevTools 支持良好,可查看响应式依赖树。recoil / jotai / zustand:DevTools 支持较弱,主要依赖 React DevTools。react-query:提供专属 DevTools,可视化查询状态、缓存和后台任务。| 场景 | 推荐方案 |
|---|---|
| 简单 UI 状态(< 5 个状态) | zustand 或 jotai(极简 API) |
| 复杂派生状态/依赖图 | recoil(selectors)或 jotai(派生 atoms) |
| 面向对象风格/响应式偏好 | mobx-react-lite |
| 企业级应用/严格可预测性 | redux + Redux Toolkit |
| 重度 API 数据交互 | react-query(必须搭配 UI 状态库) |
截至 2024 年,所有列出的包均处于积极维护状态,无官方弃用声明。但需注意:
recoil 的 GitHub 仓库更新频率已显著降低,Meta 未明确其未来路线图,新项目需谨慎评估。mobx-react-lite 是 mobx-react 的轻量替代,后者已不推荐用于新项目。react-query 管理 UI 状态 —— 它的设计目标完全不同。zustand 或 jotai —— 它们用最少的代码解决 90% 的状态问题。redux 的规范性和工具链仍是安全选择。mobx-react-lite 能最大化开发效率。状态管理没有银弹,但理解每个工具的“心智模型”和边界,能让你在正确的地方用正确的工具 —— 这才是专业前端架构的核心。
选择 redux 如果你要求严格的状态可预测性、时间旅行调试、成熟的中间件生态(如 Redux Thunk/Saga)或需要与现有 Redux 工具链集成。它适合大型企业级应用,但样板代码较多,需搭配 Redux Toolkit 才能简化开发体验。
选择 jotai 如果你希望使用极简 API 实现细粒度状态更新,且偏好原子化(atom-based)模型而非全局 store。它天然支持 React 并发特性,适合中小型项目或希望避免样板代码的团队。但注意其生态系统不如 Redux 成熟,复杂状态逻辑可能需要额外封装。
选择 mobx-react-lite 如果你倾向于面向对象的响应式编程风格,希望状态变更自动触发重渲染而无需手动订阅。它适合已有 MobX 经验的团队或需要高性能细粒度更新的场景,但需理解其“可观察对象”和“动作”概念,且对不可变数据模式支持较弱。
选择 react-query 如果你的应用重度依赖服务端数据(如 REST 或 GraphQL),需要开箱即用的缓存、去重、后台刷新、乐观更新等能力。它专为服务端状态设计,不应替代 UI 状态管理,但可与任何 UI 状态库(如 Zustand)配合使用。
选择 recoil 如果你需要强大的派生状态(selectors)和异步状态支持,且希望状态以图结构组织。它由 Meta 维护,适合大型应用中复杂的依赖关系,但学习曲线较陡,且社区活跃度近年有所下降,需评估长期维护风险。
选择 zustand 如果你想要一个零样板、Hooks 风格的全局状态管理方案,且不需要时间旅行调试。它基于单一 store 但支持模块化拆分,API 极简,适合大多数中小型项目,性能表现优秀,是现代 React 应用的轻量级首选。
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
You can use Redux together with React, or with any other view library. The Redux core is tiny (2kB, including dependencies), and has a rich ecosystem of addons.
Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app. Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.
The recommended way to start new apps with React and Redux Toolkit is by using our official Redux Toolkit + TS template for Vite, or by creating a new Next.js project using Next's with-redux template.
Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.
# Vite with our Redux+TS template
# (using the `degit` tool to clone and extract the template)
npx degit reduxjs/redux-templates/packages/vite-template-redux my-app
# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-app
We do not currently have official React Native templates, but recommend these templates for standard React Native and for Expo:
npm install @reduxjs/toolkit react-redux
For the Redux core library by itself:
npm install redux
For more details, see the Installation docs page.
The Redux core docs are located at https://redux.js.org, and include the full Redux tutorials, as well usage guides on general Redux patterns:
The Redux Toolkit docs are available at https://redux-toolkit.js.org, including API references and usage guides for all of the APIs included in Redux Toolkit.
The Redux Essentials tutorial is a "top-down" tutorial that teaches "how to use Redux the right way", using our latest recommended APIs and best practices. We recommend starting there.
The Redux Fundamentals tutorial is a "bottom-up" tutorial that teaches "how Redux works" from first principles and without any abstractions, and why standard Redux usage patterns exist.
The #redux channel of the Reactiflux Discord community is our official resource for all questions related to learning and using Redux. Reactiflux is a great place to hang out, ask questions, and learn - please come and join us there!
Redux is a valuable tool for organizing your state, but you should also consider whether it's appropriate for your situation. Please don't use Redux just because someone said you should - instead, please take some time to understand the potential benefits and tradeoffs of using it.
Here are some suggestions on when it makes sense to use Redux:
Yes, these guidelines are subjective and vague, but this is for a good reason. The point at which you should integrate Redux into your application is different for every user and different for every application.
For more thoughts on how Redux is meant to be used, please see:
The whole global state of your app is stored in an object tree inside a single store. The only way to change the state tree is to create an action, an object describing what happened, and dispatch it to the store. To specify how state gets updated in response to an action, you write pure reducer functions that calculate a new state based on the old state and the action.
Redux Toolkit simplifies the process of writing Redux logic and setting up the store. With Redux Toolkit, the basic app logic looks like:
import { createSlice, configureStore } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
incremented: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decremented: state => {
state.value -= 1
}
}
})
export const { incremented, decremented } = counterSlice.actions
const store = configureStore({
reducer: counterSlice.reducer
})
// Can still subscribe to the store
store.subscribe(() => console.log(store.getState()))
// Still pass action objects to `dispatch`, but they're created for us
store.dispatch(incremented())
// {value: 1}
store.dispatch(incremented())
// {value: 2}
store.dispatch(decremented())
// {value: 1}
Redux Toolkit allows us to write shorter logic that's easier to read, while still following the original core Redux behavior and data flow.
You can find the official logo on GitHub.
This project adheres to Semantic Versioning. Every release, along with the migration instructions, is documented on the GitHub Releases page.