mobx、react-redux、recoil、redux、redux-saga、redux-thunk、xstate 和 zustand 都是用于管理前端应用状态的 JavaScript 库,但它们在设计理念、使用方式和适用场景上存在显著差异。redux 是一个可预测的状态容器,强调单一数据源和纯函数 reducer;react-redux 是其官方 React 绑定库。redux-thunk 和 redux-saga 是 redux 的中间件,分别用于处理简单和复杂的异步逻辑。mobx 采用响应式编程模型,通过可观察对象自动追踪依赖并更新视图。recoil 是 Facebook 推出的状态管理库,以原子(atom)和选择器(selector)为核心,支持细粒度状态更新。zustand 是一个轻量级状态管理库,使用简化 API 提供类似 redux 的功能但无需样板代码。xstate 基于状态机和状态图理论,适用于具有明确状态转换逻辑的复杂交互场景。
在现代前端开发中,状态管理是核心挑战之一。面对 mobx、react-redux、recoil、redux、redux-saga、redux-thunk、xstate 和 zustand 这些工具,开发者常陷入选择困境。本文将从真实工程视角出发,通过代码示例和场景分析,帮你做出明智决策。
不同库对“状态”的理解截然不同,这直接影响开发体验。
redux 采用单一不可变状态树,通过纯函数 reducer 处理 action 更新。
// redux: 定义 reducer
const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
};
zustand 使用可变状态,但通过内部机制保证响应性,API 极简。
// zustand: 创建 store
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }))
}));
mobx 将状态视为可观察对象,通过装饰器或函数标记 observable 属性。
// mobx: 定义可观察状态
import { makeAutoObservable } from 'mobx';
class Counter {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
}
recoil 以 atom 为最小状态单元,支持派生状态 selector。
// recoil: 定义 atom 和 selector
import { atom, selector } from 'recoil';
const countAtom = atom({ key: 'count', default: 0 });
const doubledCount = selector({
key: 'doubledCount',
get: ({ get }) => get(countAtom) * 2
});
xstate 将状态建模为有限状态机,状态转换由事件驱动。
// xstate: 定义状态机
import { createMachine } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
状态管理库必须与 React 无缝协作,避免不必要的重渲染。
react-redux 提供 hooks 访问状态,需手动指定依赖以优化性能。
// react-redux: 在组件中使用
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return <button onClick={() => dispatch({ type: 'INCREMENT' })}>{count}</button>;
}
zustand 直接返回 hooks,自动按需订阅状态片段。
// zustand: 在组件中使用
function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}
mobx 需要包裹组件为 observer,自动追踪使用的 observable。
// mobx: 在组件中使用
import { observer } from 'mobx-react-lite';
const counter = new Counter();
const Counter = observer(() => (
<button onClick={() => counter.increment()}>{counter.count}</button>
));
recoil 使用内置 hooks 订阅 atom 或 selector。
// recoil: 在组件中使用
import { useRecoilState, useRecoilValue } from 'recoil';
function Counter() {
const [count, setCount] = useRecoilState(countAtom);
const doubled = useRecoilValue(doubledCount);
return <div>{doubled} <button onClick={() => setCount(c => c + 1)}>+</button></div>;
}
xstate 通过 @xstate/react 提供 hooks 与状态机交互。
// xstate: 在组件中使用
import { useMachine } from '@xstate/react';
function Toggle() {
const [state, send] = useMachine(toggleMachine);
return (
<button onClick={() => send('TOGGLE')}>
{state.value === 'active' ? 'ON' : 'OFF'}
</button>
);
}
真实应用离不开异步操作,各库提供了不同抽象层级的解决方案。
redux-thunk 允许 action creator 返回函数,直接处理异步。
// redux-thunk: 异步 action
const fetchUser = (id) => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const user = await api.getUser(id);
dispatch({ type: 'FETCH_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', error });
}
};
redux-saga 使用 generator 函数监听 action,实现复杂流程控制。
// redux-saga: 监听 action
import { takeEvery, call, put } from 'redux-saga/effects';
function* fetchUserSaga(action) {
try {
const user = yield call(api.getUser, action.payload.id);
yield put({ type: 'FETCH_SUCCESS', user });
} catch (error) {
yield put({ type: 'FETCH_ERROR', error });
}
}
function* rootSaga() {
yield takeEvery('FETCH_START', fetchUserSaga);
}
zustand 在 action 中直接使用 async/await,无需额外中间件。
// zustand: 异步 action
const useStore = create((set) => ({
user: null,
fetchUser: async (id) => {
set({ loading: true });
try {
const user = await api.getUser(id);
set({ user, loading: false });
} catch (error) {
set({ error, loading: false });
}
}
}));
mobx 在 action 中使用 async/await,配合 flow 处理复杂场景。
// mobx: 异步 action
import { flow } from 'mobx';
class UserStore {
user = null;
fetchUser = flow(function* (id) {
this.loading = true;
try {
this.user = yield api.getUser(id);
} finally {
this.loading = false;
}
});
}
recoil 使用异步 selector 或 atom effects 处理副作用。
// recoil: 异步 selector
const userQuery = selectorFamily({
key: 'userQuery',
get: (id) => async ({ get }) => {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
});
xstate 在状态机 transition 中定义异步 action 或 service。
// xstate: 异步 service
const fetchUserMachine = createMachine({
initial: 'idle',
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchUser',
onDone: { target: 'success', actions: 'assignUser' },
onError: 'failure'
}
},
// ... other states
}
}, {
services: {
fetchUser: (context, event) => api.getUser(event.id)
}
});
大型应用需要可靠的调试能力和清晰的状态流。
redux 生态提供 Redux DevTools,支持时间旅行调试和 action 日志。
// redux: 配置 devtools
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
mobx 通过 spy 和 trace 工具监控 observable 变化,但隐式依赖可能使调试复杂化。
// mobx: 启用 spy
import { spy } from 'mobx';
spy(event => console.log(event));
xstate 提供状态图可视化工具(XState Inspector),可直观查看状态转换。
// xstate: 启用 inspector
import { inspect } from '@xstate/inspect';
inspect({ iframe: false });
zustand 和 recoil 依赖 React DevTools 调试,缺乏专用状态历史工具,但代码简洁性降低了调试难度。
zustand:API 简洁,无样板代码,性能优秀。recoil:如果需要细粒度派生状态计算。redux + redux-thunk:配合 Redux Toolkit 可大幅减少样板代码。redux-saga 处理竞态、取消等高级场景。mobx:适合熟悉 OOP 的团队,但需警惕隐式依赖带来的维护成本。xstate:状态机模型能清晰表达业务规则,避免状态爆炸。recoil 项目维护状态需谨慎评估,Facebook 官方未明确其长期路线图。redux-saga 学习曲线陡峭,仅在 thunk 无法满足需求时引入。mobx 的隐式响应可能在大型项目中导致意外重渲染,务必使用 reaction 或 autorun 显式管理副作用。不要为了用而用状态管理库。对于小型应用,React Context + useReducer 可能已足够。当遇到以下情况时再考虑引入外部库:
选择时优先考虑团队熟悉度和项目复杂度。zustand 因其简洁性和性能,已成为许多新项目的默认选择;而 redux 生态在需要严格规范的大型项目中仍不可替代。
选择 redux 如果你需要一个严格、可预测且高度可调试的状态管理方案,尤其适合大型团队协作或需要时间旅行调试的场景。但其样板代码较多,新项目建议结合 Redux Toolkit 使用以简化开发。
选择 mobx 如果你需要一个响应式、声明式的状态管理方案,且团队熟悉面向对象编程。它通过自动追踪依赖减少手动订阅,适合中大型应用,但需注意其隐式依赖可能增加调试难度。务必配合 observer 或 useObserver 使用以确保 React 正确响应变化。
选择 react-redux 如果你已在使用 Redux 并需要与 React 深度集成。它提供 useSelector 和 useDispatch 等 hooks,优化了组件重渲染性能。仅在已有 Redux 架构或需要严格时间旅行调试时使用,新项目应评估更轻量方案。
选择 recoil 如果你追求细粒度状态更新和派生状态的高效计算,且项目基于 React 16.8+。它的 atom 和 selector 模型天然支持并发模式,但项目维护状态目前不够活跃,需谨慎评估长期支持风险。
选择 redux-saga 如果你的 Redux 应用包含大量复杂的异步流程(如长任务、竞态条件、取消操作)。它使用 generator 函数实现声明式副作用管理,学习曲线陡峭,仅在 thunk 无法满足需求时引入。
选择 redux-thunk 如果你的 Redux 应用只需处理简单的异步操作(如 API 调用)。它允许 action creator 返回函数,是最轻量的 Redux 异步中间件,适合大多数基础场景,但难以管理复杂流程。
选择 xstate 如果你的应用涉及明确的状态转换逻辑(如表单向导、游戏、复杂 UI 控制器)。它基于状态机理论,提供可视化状态图和强大的工具链,但概念抽象度高,不适合简单状态共享需求。
选择 zustand 如果你需要一个零样板、轻量且灵活的状态管理方案。它使用单一 store 和 hooks API,天然支持 React 并避免了 context 重渲染问题,适合中小型项目或希望快速迭代的团队。
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.