前端工程Technical Deep Dive

React 状态管理演进 —— 从 useState 到 Zustand 的完整指南

发布时间2026/03/29
分类前端工程
预计阅读3 分钟
作者吴长龙
*

状态管理的本质

01.内容

状态管理的本质

React 的核心数据流是单向的:props 向下传递,事件向上传递。但随着应用复杂度增长,状态管理成了每个开发者必须面对的问题。

阶段一:组件内部状态 useState

最简单的状态管理,每个组件维护自己的状态:

jsx snippetjsx
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

优点:简单、直观、无额外依赖 缺点:无法跨组件共享,组件树深层传递麻烦

阶段二:Context API

React 16.3 引入的 Context 解决了跨组件共享问题:

jsx snippetjsx
const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext); // 任意层级获取
  return <div className={theme}>...</div>;
}

核心问题:任何 Context 变化会导致所有消费该 Context 的组件重渲染。

优化策略

  • 拆分 Context:将不同职责的状态放入不同 Context
  • 使用 useMemo:缓存 Context 的 value
  • 状态细化:只传递需要的子集
jsx snippetjsx
// 不好:所有消费 UserContext 的组件都会重渲染
const UserContext = createContext();
const ThemeContext = createContext();

// 好:拆分后,theme 变化不会影响 user 相关组件
const UserContext = createContext();
const ThemeContext = createContext();

阶段三:Redux —— 全局状态的黄金标准

Redux 2015 年出现,定义了状态管理的最佳实践:

三大原则

  • 单一数据源:整个应用的状态存放在一个 store 中
  • 状态只读:只能通过 dispatch action 来修改状态
  • 纯函数修改:reducer 必须是纯函数
javascript snippetjavascript
// Action
const increment = () => ({ type: 'INCREMENT' });

// Reducer
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
}

// Store
const store = createStore(counter);

// 使用
store.dispatch(increment());
store.getState();

Redux Toolkit (RTK):2019 年推出的现代化 Redux 写法:

javascript snippetjavascript
import { createSlice, configureStore } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
  },
});

export const { increment, decrement } = counterSlice.actions;

const store = configureStore({
  reducer: counterSlice.reducer,
});

RTK 内置了 Immer 支持,可以直接"可变式"修改状态。

React-Redux hooks

jsx snippetjsx
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector(state => state.value);
  const dispatch = useDispatch();
  return <button onClick={() => dispatch(increment())}>{count}</button>;
}

阶段四:轻量级方案崛起

随着应用复杂度分化,出现了更轻量的方案:

#### Zustand

javascript snippetjavascript
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
}));

// 使用 - 无需 Provider!
function Counter() {
  const { count, increment } = useStore();
  return <button onClick={increment}>{count}</button>;
}

优点

  • 零配置,无需 Provider 包裹
  • 极小的体积(~1KB)
  • 灵活的选择器,避免不必要重渲染
  • 支持中间件

#### Jotai

原子化状态管理,适合复杂表单和精细控制:

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

const countAtom = atom(0);
const doubledAtom = atom((get) => get(countAtom) * 2);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  const [doubled] = useAtom(doubledAtom);
  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>{count}</button>
      <p>Double: {doubled}</p>
    </div>
  );
}

适用场景:需要细粒度控制、派生状态多的场景。

#### Valtio

代理式状态管理,代码极其简洁:

javascript snippetjavascript
import { proxy } from 'valtio';

const state = proxy({ count: 0 });

function Counter() {
  const snap = useSnapshot(state);
  return <button onClick={() => { state.count++; }}>{snap.count}</button>;
}

状态管理选型指南

方案适用场景优点缺点
useState简单组件、局部状态无依赖、直观无法跨组件共享
Context主题、语言、全局配置简单、无需库重渲染问题
Redux大型复杂应用生态完善、调试强大样板代码多
Zustand中小型应用轻量、简单功能有限
Jotai细粒度状态控制原子化、灵活学习曲线
Valtio快速原型、简单需求极简灵活性差

最佳实践

  • 优先本地状态:能用 useState 解决的不要用全局状态
  • 状态最近原则:状态应该放在离使用它最近的地方
  • 避免状态冗余:派生数据用 useMemo,不要存重复状态
  • 选择合适的复杂度:不要过度工程化

理解每种方案的适用场景,才能做出正确的技术决策。