React 优雅地熟悉爱你组件间的状态共享
作者:JJCTO袁龙
本文主要介绍了React优雅地熟悉爱你组件间的状态共享,解决prop drilling、重渲染和状态混乱问题,让状态管理既清晰又高效,感兴趣的可以了解一下
一句话总结:用「Context 拆分 + useReducer 集中 + 第三方方案兜底」三件套,让组件间状态共享既清晰又高效,不再 prop drilling 到崩溃!
一、问题背景:为什么组件间共享状态总让人头疼?
React 是单向数据流,状态默认只能在当前组件内使用。当多个组件需要共享同一个状态时,最直觉的做法是 prop drilling(逐层传递 props):
// ❌ prop drilling:状态要穿好几层组件
function App() {
const [user, setUser] = useState({ name: '张三' });
return <Layout user={user} setUser={setUser} />; // 第1层
}
function Layout({ user, setUser }) {
return <Sidebar user={user} setUser={setUser} />; // 第2层
}
function Sidebar({ user, setUser }) {
return <UserCard user={user} setUser={setUser} />; // 第3层
}
function UserCard({ user, setUser }) {
return <div>{user.name}</div>; // 终于用上了
}
层数一多,中间组件被迫传递自己根本不用的 props,代码又长又脆弱,任何中间层重构都会断掉传递链。
本质:缺少跨层级的状态共享机制。
二、5 大高频使用场景 & 修复代码
① prop drilling 传递到天荒地老
// ❌ 状态穿 5 层,中间组件全是透传
function App() {
const [theme, setTheme] = useState('light');
return <A theme={theme} setTheme={setTheme} />;
}
function A({ theme, setTheme }) { return <B theme={theme} setTheme={setTheme} />; }
function B({ theme, setTheme }) { return <C theme={theme} setTheme={setTheme} />; }
function C({ theme, setTheme }) {
return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>{theme}</button>;
}
修复:用 Context 跨层共享
import { createContext, useContext, useState } from 'react';
// ✅ 创建 Context
const ThemeContext = createContext();
// ✅ Provider 包裹整个应用
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<A />
</ThemeContext.Provider>
);
}
function A() { return <B />; } // ✅ 不用透传
function B() { return <C />; } // ✅ 不用透传
function C() {
// ✅ 直接消费 Context,跳过中间层
const { theme, setTheme } = useContext(ThemeContext);
return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>{theme}</button>;
}
② Context value 每次都变,导致所有消费者都重渲染
const AppContext = createContext();
function App() {
const [count, setCount] = useState(0);
const [theme, setTheme] = useState('light');
// ❌ 每次渲染都创建新对象,所有消费组件都重渲染
return (
<AppContext.Provider value={{ count, theme, setCount, setTheme }}>
<Counter />
<ThemeToggle />
</AppContext.Provider>
);
}
修复:拆分 Context + useMemo 缓存 value
import { createContext, useContext, useMemo, useState } from 'react';
// ✅ 拆分成两个独立 Context
const CountContext = createContext();
const ThemeContext = createContext();
function App() {
const [count, setCount] = useState(0);
const [theme, setTheme] = useState('light');
// ✅ useMemo 缓存 value 对象
const countValue = useMemo(() => ({ count, setCount }), [count]);
const themeValue = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<CountContext.Provider value={countValue}>
<ThemeContext.Provider value={themeValue}>
<Counter />
<ThemeToggle />
</ThemeContext.Provider>
</CountContext.Provider>
);
}
③ 多个状态互相联动,逻辑分散难维护
function Cart() {
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
const [discount, setDiscount] = useState(0);
const [finalPrice, setFinalPrice] = useState(0);
// ❌ addItem 里要手动更新 4 个 state,逻辑分散容易出错
function addItem(item) {
setItems(prev => [...prev, item]);
setTotal(prev => prev + item.price);
setFinalPrice(total + item.price - discount); // ❌ total 是旧值
}
}
修复:用 useReducer 集中管理状态
import { useReducer } from 'react';
// ✅ 定义 reducer
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
const items = [...state.items, action.item];
const total = items.reduce((sum, i) => sum + i.price, 0);
return {
...state,
items,
total,
finalPrice: total - state.discount,
};
case 'SET_DISCOUNT':
return {
...state,
discount: action.value,
finalPrice: state.total - action.value,
};
default:
return state;
}
}
function Cart() {
// ✅ 一个 reducer 管理所有关联状态
const [state, dispatch] = useReducer(cartReducer, {
items: [],
total: 0,
discount: 0,
finalPrice: 0,
});
function addItem(item) {
dispatch({ type: 'ADD_ITEM', item }); // ✅ 自动联动所有状态
}
return (
<div>
<button onClick={() => addItem({ name: '手机', price: 5999 })}>加购</button>
<p>总价:{state.total},实付:{state.finalPrice}</p>
</div>
);
}
④ Context + useReducer 组合实现全局状态
// ✅ Context 传 dispatch,避免 value 频繁变化
import { createContext, useContext, useReducer } from 'react';
const StoreContext = createContext();
const initialState = { user: null, cart: [] };
function storeReducer(state, action) {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.user };
case 'ADD_TO_CART':
return { ...state, cart: [...state.cart, action.item] };
default:
return state;
}
}
// ✅ Provider 只传 dispatch,引用永远不变
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(storeReducer, initialState);
return (
<StoreContext.Provider value={{ state, dispatch }}>
{children}
</StoreContext.Provider>
);
}
// ✅ 消费者
function UserBar() {
const { state, dispatch } = useContext(StoreContext);
return <div>{state.user?.name || '未登录'}</div>;
}
function AddToCartButton({ item }) {
const { dispatch } = useContext(StoreContext);
return <button onClick={() => dispatch({ type: 'ADD_TO_CART', item })}>加购</button>;
}
⑤ 大型项目状态管理混乱,Context 嵌套地狱
// ❌ Context 层层嵌套,难以维护
function App() {
return (
<UserProvider>
<ThemeProvider>
<CartProvider>
<LanguageProvider>
<NotificationProvider>
<AppContent />
</NotificationProvider>
</LanguageProvider>
</CartProvider>
</ThemeProvider>
</UserProvider>
);
}
修复:中型项目用 Zustand,大型项目用 Redux Toolkit
// ✅ Zustand:轻量级状态管理,无需 Provider
import { create } from 'zustand';
const useStore = create((set) => ({
user: null,
cart: [],
theme: 'light',
setUser: (user) => set({ user }),
addToCart: (item) => set((state) => ({ cart: [...state.cart, item] })),
toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}));
// ✅ 任何组件直接用,无需 Provider 包裹
function UserBar() {
const user = useStore((state) => state.user);
const setUser = useStore((state) => state.setUser);
return <div>{user?.name || '未登录'}</div>;
}
function CartButton({ item }) {
const addToCart = useStore((state) => state.addToCart);
return <button onClick={() => addToCart(item)}>加购</button>;
}
三、万能兜底:Context + useReducer + 第三方方案
| 项目规模 | 推荐方案 | 适用场景 |
|---|---|---|
| 小型项目(3-5 个组件共享) | Context + useState | 主题、语言、用户信息 |
| 中型项目(状态联动) | Context + useReducer | 购物车、表单状态 |
| 中型项目(多模块) | Zustand | 轻量、无 Provider、选择性订阅 |
| 大型项目(团队协作) | Redux Toolkit | DevTools 时间旅行、中间件 |
| 跨组件状态不多 | prop drilling + 组合组件 | 简单场景不要过度工程 |
万能模板:
import { createContext, useContext, useReducer, useMemo } from 'react';
// ✅ 1. 创建 Context
const AppContext = createContext();
// ✅ 2. 定义 reducer
function appReducer(state, action) {
switch (action.type) {
case 'SET_DATA':
return { ...state, data: action.data };
case 'UPDATE':
return { ...state, [action.field]: action.value };
default:
return state;
}
}
// ✅ 3. Provider 组件
function AppProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, { data: null });
// ✅ useMemo 缓存 value
const value = useMemo(() => ({ state, dispatch }), [state]);
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}
// ✅ 4. 自定义 Hook(可选,封装消费逻辑)
function useApp() {
const { state, dispatch } = useContext(AppContext);
if (!state) throw new Error('useApp must be used within AppProvider');
return { state, dispatch };
}
export { AppProvider, useApp };
四、预防 checklist
- 状态只穿 2 层以内 → 直接 prop drilling,不用 Context
- 状态穿 3 层以上 → 用 Context
- 多个状态联动 → 用 useReducer 集中管理
- Context value 变化导致重渲染 → 用 useMemo 缓存 + 拆分 Context
- 大型项目嵌套地狱 → 用 Zustand 或 Redux Toolkit
- Context 消费逻辑重复 → 封装自定义 Hook(如 useApp())
- 不要在 Context Provider 的 value 里直接写对象字面量 → 用 useMemo
五、一句话总结
「组件间状态共享」= 需要跨层级传递,但不想逐层透传。
用「Context 跨层共享 + useReducer 集中管理 + Zustand/RTK 兜底大型项目」三件套,让组件间状态共享既清晰又高效!
到此这篇关于React 优雅地熟悉爱你组件间的状态共享的文章就介绍到这了,更多相关React 组件间状态共享内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
