ToolkitX
知识库工具箱

状态管理

Context API, useReducer, Zustand 入门

25min·进阶

01. 状态管理怎么选——不是越复杂越好

React 的状态管理从简单到复杂有不同方案,关键是根据实际需要选而不是追热门: useState——组件内部的状态。最基础,适合单个组件的简单状态。 useReducer——useState 的升级版,用 reducer 函数管理复杂状态逻辑。多个子值相互依赖时比 useState 清晰。 Context API——跨组件共享状态,不用一层层传 props。但性能上 Context 值变了所有消费者都重渲染。 Zustand——轻量外部状态库,API 极简没有 Provider 包裹、没有样板代码。适合中小项目。 Redux Toolkit——完整的状态管理方案,有 DevTools、中间件、异步处理。适合大型项目和团队协作。 一般来说:小项目 Zustand 够用,中项目 Context + useReducer 够用,大项目考虑 Redux Toolkit。不要上来就 Redux。

02. useState 与 useReducer——局”,状态管理

useState 是最简单的状态 hook:const [value, setValue] = useState(initialValue)。setValue 触发重渲染。 useReducer 适用场景:状态逻辑复杂(多个状态相互依赖)、下一个状态依赖上一个状态。写法:const [state, dispatch] = useReducer(reducer, initialState)。reducer 是纯函数 (state, action) => newState。 什么时候该用 useReducer 而不是 useState: 1. 一个操作要同时改多个状态(如表单提交后清空成功消息和表单值) 2. 下一个状态需要基于上一个状态计算(如计数器、分步表单) 3. 状态更新逻辑需要被测试(reducer 是纯函数,单元测试很容易写)
javascript
// useState
const [count, setCount] = useState(0);
// 基于前一个值更新
setCount(prev => prev + 1);

// useReducer
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'decrement':
      return { ...state, count: state.count - 1 };
    case 'setName':
      return { ...state, name: action.payload };
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(reducer, {
  count: 0,
  name: ''
});

03. Context API——跨组件共享数据

Context 解决的是 props drilling 问题——父组件要给很深的子组件传数据,中间每一层都要转发 props。Context 让你在组件树里创建一个数据隧道。 三步:createContext 建一个 context;在父组件用 Provider 提供数据;在子组件用 useContext 读取数据。 Context 的局限:Provider 的值变了,所有用到这个 Context 的组件都会重新渲染——即使它们只用了 Context 里某个没变的值。解决方法:拆分 Context(把不同关注点的状态放不同 Context);或用 useMemo 记忆传给 Context 的值。 Context 不等于状态管理——它只是依赖注入机制。实际的状态管理(更新、派生)还需要 useState 或外部库。Context 负责传递,状态管理库负责存储和更新。
jsx
// 创建 Context
const ThemeContext = createContext('light');

// 提供数据
function App() {
  const [theme, setTheme] = useState('light');
  const value = useMemo(() => ({ theme, setTheme }), [theme]);

  return (
    <ThemeContext.Provider value={value}>
      <MainContent />
    </ThemeContext.Provider>
  );
}

// 消费数据
function Button() {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Current: {theme}
    </button>
  );
}
Context 的值每次渲染都是新对象的话(即使内容没变),消费者也会全部重渲染。用 useMemo 缓存 context value 对象。

04. Zustand——轻量状态库入门

Zustand 是当下 React 社区最受青睐的轻量状态库之一。它最吸引人的是没有 Provider 包裹、没有样板代码、API 就是调用函数。 核心概念:create 函数创建一个 store,里面定义了状态和修改状态的方法。组件里直接调用 useStore(selector) 拿需要的部分。 selector 的作用——只选择你需要的状态。Zustand 只在 selector 返回的值变化时才重渲染组件,避免了 Context 的全量渲染问题。 异步操作也很直接——store 的方法可以是 async 函数的。直接 async set 就行,不需要中间件。 对比 Redux:Zustand 不需要 reducer、dispatch、action 这些概念。定义状态和方法,直接在组件里用。代码少很多。
jsx
// Zustand store
import { create } from 'zustand';

const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

// 组件里使用
function Counter() {
  const count = useCounterStore((s) => s.count);
  const increment = useCounterStore((s) => s.increment);

  return (
    <div>
      <span>{count}</span>
      <button onClick={increment}>+</button>
    </div>
  );
}
Zustand 的 selector 是默认浅比较的——只有你选出来的值变了才重渲染。不需要像 Context 那样手动 useMemo。

05. 什么时候需要全局状态、什么时候不需要

不是所有状态都要拉成全局状态。很多状态放在组件内部就够了: 组件内部状态——只有这一个组件需要关心的数据。比如表单输入值、展开/收起状态、弹窗的显示。用 useState。 组件树共享状态——父子组件之间传 props 就行。如果只传 1~2 层,不要因为怕 prop drilling 就上全局状态。 全局状态——多个完全无关的组件需要共享的数据。比如用户登录信息、主题设置、购物车。这种才放到全局状态里。 服务器状态——API 返回的数据(用户列表、订单详情)。不要手动存到 Redux/Zustand 里自己管理缓存。用 React Query 或 SWR——它们自带缓存、重新获取、乐观更新。 表单状态——表单的复杂状态用 React Hook Form 比手动 useReducer 好得多。它是表单领域的专项工具。
jsx
// 服务器状态——用 React Query,不要手动管理
import { useQuery } from '@tanstack/react-query';

function UserList() {
  const { data, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(r => r.json()),
  });

  if (isLoading) return <div>Loading...</div>;
  return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
React Query / SWR 这种服务器状态库让你不用操心缓存、loading、refetch 这些事。它们是全局状态库里很大一块内容的最佳替代品。

知识测验

1/5正确 0

useState 和 useReducer 什么时候该用后者?

下一节

路由管理

下一节