← 返回题目列表

React 项目中如何选择状态管理方案?

高频 中等 第 3 / 27 题 更新于 2026/07/29
React状态管理ContextReduxZustand

简化版

React 状态管理要先区分状态类型:本地 UI 状态放组件内,跨层级共享可用 Context,复杂客户端状态可用 Redux/Zustand/Jotai,服务端数据缓存应考虑 React Query、SWR 等。不要把所有状态都塞进全局 store。

详细版

常见分类:

  • 组件本地状态:弹窗开关、输入框值,用 useState
  • 局部复杂状态:复杂表单、多步骤流程,用 useReducer
  • 跨层级共享状态:主题、语言、当前用户,用 Context。
  • 大型客户端状态:复杂业务 store,用 Redux、Zustand 等。
  • 服务端状态:接口数据、缓存、刷新、失效,用 React Query、SWR。

面试重点是说明“状态类型决定方案”,而不是直接说某个库最好。

完整版教学

一、先区分状态类型,而不是先选库

状态管理最常见的问题是上来就问“用 Redux 还是 Zustand”。 真正的工程判断应该先问:这个状态属于谁、生命周期多长、是否跨组件、是否来自服务端。 不同状态有不同归宿。

状态来源
  -> 组件内部临时状态
  -> 客户端共享状态
  -> 服务端远程数据
  -> URL 状态

例如搜索关键词可能应该进 URL query,而不是全局 store。 接口列表数据可能应该交给服务端状态库,而不是手写 Redux 缓存。 分类正确,方案自然会清楚。

二、本地 UI 状态优先留在组件内

弹窗开关、hover、输入框临时值、Tab 当前项,这些通常只影响一个组件或局部子树。 用 useStateuseReducer 放在组件内最简单。 放进全局 store 会扩大影响范围。

function DialogButton() {
  const [open, setOpen] = useState(false);
  return <button onClick={() => setOpen(true)}>打开</button>;
}

如果 1 个组件使用,就不要做成全局。 如果 3 到 5 个兄弟组件共享,可以先考虑状态提升到共同父组件。 只有跨很多层或跨页面时,再考虑 Context 或外部 store。

三、Context 适合低频共享,不适合高频大状态

Context 适合主题、语言、当前登录用户、权限能力这类低频共享数据。 它解决的是“避免层层传 props”。 但 Context value 变化会让消费它的组件重新渲染,复杂高频状态直接塞 Context 可能带来性能问题。

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Page />
    </ThemeContext.Provider>
  );
}
状态是否适合 Context原因
theme适合低频、全局读取
locale适合低频、全局读取
鼠标坐标不适合高频变化
大型表格数据谨慎更新范围大

如果 Context value 每秒更新 60 次,很多消费者都会受影响。 这时应该考虑拆 Context、使用 selector 型 store,或保持状态局部化。

四、复杂客户端状态适合外部 store

当状态跨页面共享、更新动作复杂、需要 devtools、持久化、中间件或更细粒度订阅时,可以考虑外部状态库。 Redux 生态成熟、约束强;Zustand 更轻量;Jotai/Recoil 更偏原子化模型。 没有绝对最好,只有适合场景。

Redux:
  大型团队、强约束、复杂调试
Zustand:
  轻量 store、低样板代码
Jotai:
  原子状态、细粒度组合

数字例子:如果一个后台有 20 个业务模块、50 多个跨页面状态、需要审计 action,Redux 的约束可能更有价值。 如果只是几个全局状态,Zustand 可能更轻。 选型要看团队规模和状态复杂度。

五、服务端状态不应该简单当成本地全局状态

接口数据和本地 UI 状态很不一样。 它有加载中、错误、缓存、重新请求、失效、并发、分页、乐观更新等问题。 这类状态常被称为 server state。

服务端状态问题:
  loading / error
  cache / stale
  refetch
  pagination
  optimistic update

如果手写全局 store 管所有接口缓存,很容易越写越复杂。 React Query、SWR 这类库就是为服务端状态设计的。 面试回答中把 client state 和 server state 分开,是一个很重要的加分点。

六、URL 状态适合表达可分享的页面状态

分页、筛选、排序、搜索词有时应该放进 URL。 这样刷新页面不丢,链接可以分享,也更利于回退前进。 不要把所有页面状态都放内存 store。

/users?page=2&keyword=react&role=admin

记忆钩子:组件状态问“谁用”,服务端状态问“谁拥有”,URL 状态问“要不要分享”。

如果状态需要用户复制链接后仍然保留,就优先考虑 URL。 如果只是弹窗开关,就没必要污染 URL 或全局 store。

七、常见误区与追问

  • 误区:React 项目都必须上 Redux。 小项目或局部状态多的项目不一定需要 Redux,过早引入会增加样板代码。
  • 误区:Context 可以替代所有状态库。 Context 适合低频共享,高频复杂状态可能导致性能和组织问题。
  • 误区:接口数据放 Redux 就是最佳实践。 服务端状态有缓存和失效问题,React Query、SWR 等工具更专业。
  • 追问:什么时候状态提升就够了? 只有少量兄弟组件共享时,把状态提到共同父组件通常比全局 store 简单。
  • 追问:如何判断状态是否全局? 看是否跨页面、是否多处读写、是否需要持久化或统一调试。
  • 追问:为什么 URL 也是状态管理? URL 能表达可分享、可刷新恢复的页面状态,如分页、筛选和排序。

八、加强记忆

这题不要从库名开始答,要从状态分类开始答。本地 UI 状态留组件内,复杂局部状态用 reducer,低频跨层共享用 Context,大型客户端状态用外部 store,接口缓存用服务端状态库,可分享筛选分页放 URL。能这样分类,选型就很稳。