React 全局状态进阶指南:用 useReducer + Context,写出稳如老狗的共享状态系统

58 阅读4分钟

🔥 再也不用 props 疯狂套娃了!React 状态管理,不需要 Redux 也能很香。


🧭 导语:当 useState 不再够用

刚入门 React 时,一切看起来都很简单:
useState 管状态,组件里写点逻辑,网页就动起来了,嘎嘎香。

可当你做个稍微复杂点的功能,比如:

  • 购物车里的商品数量,多个组件都要用;
  • 登录状态,顶部菜单、个人页都得感知;
  • 页面主题,暗黑 or 明亮,全站同步切换;

这时候你可能就绷不住了:

👉🏻 状态只能一级一级地传 props,A 传给 B,B 再传给 C,C 再给 D……
👉🏻 某个状态被很多组件依赖,一改就一片重渲染,调 bug 疯狂。

所以今天我们来学习一个轻量但专业的方案:


🧩 一套组合拳:useReducer + useContext + 自定义 Hook

它的核心目标是:

💡 提取出全局共享的状态逻辑,避免 props 传递地狱,让组件之间自由访问共享状态。

下面是它们的分工:

  • useReducer: 管理状态结构 + 状态更新逻辑(就像 Redux)
  • useContext: 构建一个“状态仓库”,供全局访问
  • 自定义 Hook:做一层封装,隐藏复杂逻辑,暴露易用 API

接下来我们一步步构建一个「待办事项(Todo List)」应用,来实践这套组合方案。


✅ 场景目标:Todo 列表功能拆解

我们希望实现的功能:

  • 新增一条待办事项
  • 标记事项为完成/未完成
  • 删除一项事项
  • 多个组件都能共享操作这份 Todo 列表

🛠 项目结构 & 技术拆解

  • 状态形态:todos 是一个数组,包含 { id, text, done } 的对象
  • 状态更新:统一走 reducer 函数
  • 状态共享:基于 Context
  • 状态调用:封装到 Hook

🔧 第一步:创建 Context 容器

// context/TodoContext.js
import { createContext } from 'react';

export const TodoContext = createContext(null);

这就是我们状态“仓库”的壳子,稍后我们会把状态和操作都挂进来。


🧠 第二步:定义 reducer(状态逻辑规则)

// reducers/todoReducer.js
export default function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD':
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case 'TOGGLE':
      return state.map(item =>
        item.id === action.id ? { ...item, done: !item.done } : item
      );
    case 'DELETE':
      return state.filter(item => item.id !== action.id);
    default:
      return state;
  }
}

📌 解读:

  • 状态是不可变的,所有操作都返回新数组;
  • 操作必须明确:通过 type 和 payload 来驱动。

🧰 第三步:封装 useTodos Hook(核心业务封装)

// hooks/useTodos.js
import { useReducer } from 'react';
import todoReducer from '../reducers/todoReducer';

export function useTodos(initial = []) {
  const [todos, dispatch] = useReducer(todoReducer, initial);

  const add = (text) => dispatch({ type: 'ADD', text });
  const toggle = (id) => dispatch({ type: 'TOGGLE', id });
  const del = (id) => dispatch({ type: 'DELETE', id });

  return { todos, add, toggle, del };
}

优势:

  • 所有操作都内聚在这里;
  • 不暴露 dispatch,外部不能乱操作状态;
  • 实际组件里调用极其清爽。

🪝 第四步:封装 useTodoContext(简化访问)

// hooks/useTodoContext.js
import { useContext } from 'react';
import { TodoContext } from '../context/TodoContext';

export const useTodoContext = () => useContext(TodoContext);

使用方式:

const { todos, add, toggle, del } = useTodoContext();

简洁、直观。


🧱 第五步:App 根组件绑定 Provider

// App.jsx
import { TodoContext } from './context/TodoContext';
import { useTodos } from './hooks/useTodos';
import AddTodo from './components/AddTodo';
import TodoList from './components/TodoList';

export default function App() {
  const todoLogic = useTodos([]);

  return (
    <TodoContext.Provider value={todoLogic}>
      <h2>My Todos</h2>
      <AddTodo />
      <TodoList />
    </TodoContext.Provider>
  );
}

Provider 是整个状态系统的入口,所有子组件都可以访问它共享的状态。


➕ 第六步:新增 Todo 的组件

// components/AddTodo.jsx
import { useState } from 'react';
import { useTodoContext } from '../hooks/useTodoContext';

export default function AddTodo() {
  const [input, setInput] = useState('');
  const { add } = useTodoContext();

  const handleAdd = (e) => {
    e.preventDefault();
    if (input.trim()) {
      add(input.trim());
      setInput('');
    }
  };

  return (
    <form onSubmit={handleAdd}>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button type="submit">Add</button>
    </form>
  );
}

📋 第七步:展示 Todo 列表的组件

// components/TodoList.jsx
import { useTodoContext } from '../hooks/useTodoContext';

export default function TodoList() {
  const { todos, toggle, del } = useTodoContext();

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>
          <span
            style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
            onClick={() => toggle(todo.id)}
          >
            {todo.text}
          </span>
          <button onClick={() => del(todo.id)}>Delete</button>
        </li>
      ))}
    </ul>
  );
}

🧠 总结:这一套值不值得用?

✅ 优点:

  • 组织清晰:状态逻辑集中管理,行为明确。
  • 完全响应式:任何修改,组件自动响应更新。
  • 轻量不依赖库:0 依赖,纯 React 实现。
  • 适合中小项目:功能齐全但足够轻量。

🚀 延伸思考:还能干啥?

只要是全局可共享的状态,几乎都能套用这套组合:

  • 登录用户信息管理
  • 黑暗模式切换
  • 多页签状态控制
  • 表单多步骤流程状态

🧨 写在最后

如果你觉得 Redux 上手太重,MobX 学起来太玄,不妨从这套轻量方案起步。

它的思想是:

让状态管理可控、可预测、可共享

当你熟练掌握 useReducer + useContext,再借助自定义 Hook 进行封装 —— 你就真正跨过了 React 状态管理的门槛!


如果这篇文章有帮到你,不妨点个赞、留个言👇:

🗯️ “想看异步数据怎么配合这套玩?”
🗯️ “能不能支持持久化和懒加载状态?”
🗯️ “有没有结合 TypeScript 的版本?”

我都可以安排!

让我们一起写出更优雅、可维护、可拓展的 React 应用!