在引入任何第三方状态管理库之前,React 本身已经提供了一套完整的状态管理原语。通过组合 useState、useContext 和 useReducer,你可以在不安装额外依赖的情况下,管理从简单到中等复杂度的应用状态。这套原生方案的最大优势是零额外依赖、学习成本低、与 React 深度集成。
三个 API 的职责分工
- useState:管理组件内部的局部状态,适合简单的、独立的状态值。
- useContext:将状态从祖先组件传递到任意深度的子组件,打破 Props 层层传递的“下钻”问题。
- useReducer:当状态更新逻辑变得复杂(多个子值、依赖前一状态),提供可预测的状态更新模式,类似 Redux 的 Reducer 概念。
三者组合使用,可以构成一个轻量级但功能完备的全局或模块级状态管理体系。
从 Props 下钻到 Context 提升
在没有 Context 的情况下,如果多个嵌套组件需要共享某个状态,必须通过 Props 一层层手动传递,称为 Props Drilling。这会让中间组件被迫接收并不关心的属性。
// ❌ Props 层层传递,中间组件耦合了无关状态
function GrandParent() {
const [theme, setTheme] = useState('light');
return <Parent theme={theme} onToggle={() => setTheme(t => t === 'light' ? 'dark' : 'light')} />;
}
function Parent({ theme, onToggle }) {
return <Child theme={theme} onToggle={onToggle} />;
}
function Child({ theme, onToggle }) {
return <div className={theme}><button onClick={onToggle}>切换</button></div>;
}
使用 createContext 和 useContext 可以优雅解决:
// ✅ 通过 Context 直接向深层组件注入状态
const ThemeContext = createContext();
function GrandParent() {
const [theme, setTheme] = useState('light');
const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggle }}>
<Parent />
</ThemeContext.Provider>
);
}
function Parent() {
return <Child />;
}
function Child() {
const { theme, toggle } = useContext(ThemeContext);
return <div className={theme}><button onClick={toggle}>切换</button></div>;
}
Parent 完全无需关心 theme,Child 直接从 Context 中获取所需数据。这是原生方案中实现跨层级共享的核心机制。
useReducer:管理复杂状态逻辑
当状态更新依赖于前一个状态,或者一个操作会同时修改多个状态值时,useState 的回调写法会很分散。useReducer 可以将所有更新逻辑集中到一个纯函数中,让状态变更可预测、可测试。
典型示例:管理一个计数器,以及它的历史记录。
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {
...state,
count: state.count + 1,
history: [...state.history, state.count + 1]
};
case 'decrement':
return {
...state,
count: state.count - 1,
history: [...state.history, state.count - 1]
};
case 'reset':
return { count: 0, history: [] };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, history: [] });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
<p>History: {state.history.join(', ')}</p>
</div>
);
}
reducer 是一个纯函数,接收当前状态和描述“要做什么”的 action,返回新状态。这使得状态变更完全可预测,也便于编写单元测试。
combine:构建全局状态管理
将 useReducer 与 useContext 结合,可以获得一个类似 Redux 的全局状态管理方案,且零依赖。
步骤:
- 创建一个 Context。
- 在顶层组件中使用
useReducer管理全局状态。 - 将状态和
dispatch函数通过 Context 向下传递。 - 任何后代组件通过
useContext读取状态、触发更新。
下面是一个购物车全局管理的简化示例:
// 定义 reducer 和 Context
const CartContext = createContext();
function cartReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, action.payload];
case 'remove':
return state.filter(item => item.id !== action.payload.id);
case 'clear':
return [];
default:
return state;
}
}
// 顶层 Provider 组件
function CartProvider({ children }) {
const [cart, dispatch] = useReducer(cartReducer, []);
return (
<CartContext.Provider value={{ cart, dispatch }}>
{children}
</CartContext.Provider>
);
}
// 自定义 Hook,方便消费
function useCart() {
const context = useContext(CartContext);
if (!context) {
throw new Error('useCart must be used within a CartProvider');
}
return context;
}
任何组件只需调用 useCart() 即可访问购物车数据或派发更新:
function Product({ product }) {
const { dispatch } = useCart();
const addToCart = () => dispatch({ type: 'add', payload: product });
return <button onClick={addToCart}>加入购物车</button>;
}
function CartSummary() {
const { cart } = useCart();
return <p>购物车中共有 {cart.length} 件商品</p>;
}
原生方案的适用场景与局限
适用场景:
- 小型到中型应用,全局状态结构简单。
- 模块级别的共享状态(如多步骤表单、购物车),不必要引入全局 Store。
- 团队不想引入额外库,追求最小化依赖。
局限性:
- 当应用规模增大,Context 的频繁更新会导致所有消费此 Context 的组件重新渲染,即使它们只用到了状态的一部分。需要手动使用
useMemo、拆分 Context 来优化。 - 缺少中间件、类似 Redux DevTools、时间旅行调试等高级特性。
- 状态逻辑较复杂时,
reducer会逐渐膨胀,不如 Zustand 等方案原子化。
性能优化:拆分 Context 避免不必要渲染
许多开发者错误地将所有全局状态扔进一个巨大的 Context,导致一个状态变动触发所有消费者重渲染。正确的做法是按职责拆分 Context。
// 按领域拆分成两个 Context
const AuthContext = createContext();
const CartContext = createContext();
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [cart, dispatch] = useReducer(cartReducer, []);
return (
<AuthContext.Provider value={{ user, setUser }}>
<CartContext.Provider value={{ cart, dispatch }}>
{children}
</CartContext.Provider>
</AuthContext.Provider>
);
}
这样只有当 user 变化时,AuthContext 的消费者才重渲染;只有 cart 变化时,CartContext 的消费者才受影响。这是原生方案规避性能问题的关键手段。
总结
useState + useContext + useReducer 构成了 React 的原生状态管理铁三角。它无需任何第三方库即可实现从局部到全局的状态共享,是理解所有状态管理方案的基础。当应用复杂度持续上升,出现频繁的全局状态变动和性能瓶颈时,再考虑引入 Zustand、Redux Toolkit 等更专业的方案。但无论用哪一款,核心原理都与这套原生模式一脉相承。