02-Hooks完全指南——03-useContext 与跨组件通信 3-useContext 与跨组件通信一、Context 基础1.1 什么是 ContextContext 提供了一种在组件树中传递数据的方法无需手动逐层传递 props。它解决了props drilling属性钻取的问题。1.2 何时使用 Context场景是否使用 Context说明主题深色/浅色模式✅ 推荐全局样式偏好用户认证信息✅ 推荐登录状态、用户资料语言/本地化✅ 推荐多语言切换表单状态⚠️ 谨慎简单表单用 propsUI 状态模态框开关❌ 不推荐用状态提升路由状态✅ 推荐路由库内部使用二、Context API 基础用法2.1 创建和使用 Context// 1. 创建 Context const ThemeContext React.createContext(light); // 2. 提供者组件 function ThemeProvider({ children }) { const [theme, setTheme] useState(light); return ( ThemeContext.Provider value{{ theme, setTheme }} {children} /ThemeContext.Provider ); } // 3. 消费 Context函数组件 function ThemedButton() { const { theme, setTheme } useContext(ThemeContext); return ( button style{{ background: theme light ? #fff : #333, color: theme light ? #333 : #fff }} onClick{() setTheme(theme light ? dark : light)} 切换主题 /button ); } // 4. 使用 function App() { return ( ThemeProvider ThemedButton / /ThemeProvider ); }2.2 默认值// 默认值仅在 Provider 缺失时使用 const UserContext React.createContext({ name: 游客, role: guest }); function WelcomeMessage() { const user useContext(UserContext); return div欢迎, {user.name}!/div; } // 没有 Provider使用默认值 WelcomeMessage / // 显示 欢迎, 游客!三、Context 进阶用法3.1 多个 Contextconst ThemeContext React.createContext(); const UserContext React.createContext(); const LanguageContext React.createContext(); function App() { const [theme, setTheme] useState(light); const [user, setUser] useState(null); const [language, setLanguage] useState(zh-CN); return ( ThemeContext.Provider value{{ theme, setTheme }} UserContext.Provider value{{ user, setUser }} LanguageContext.Provider value{{ language, setLanguage }} Dashboard / /LanguageContext.Provider /UserContext.Provider /ThemeContext.Provider ); } function Dashboard() { const { theme } useContext(ThemeContext); const { user } useContext(UserContext); const { language } useContext(LanguageContext); return ( div className{theme-${theme}} h1{user?.name || 未登录}/h1 p当前语言: {language}/p /div ); }3.2 自定义 Provider 组件// 创建 Context const AuthContext React.createContext(); // 自定义 Provider export function AuthProvider({ children }) { const [user, setUser] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); const login async (email, password) { try { setLoading(true); const response await fetch(/api/login, { method: POST, body: JSON.stringify({ email, password }) }); const data await response.json(); setUser(data.user); localStorage.setItem(token, data.token); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const logout () { setUser(null); localStorage.removeItem(token); }; const value { user, loading, error, login, logout, isAuthenticated: !!user }; return ( AuthContext.Provider value{value} {children} /AuthContext.Provider ); } // 自定义 Hook 方便使用 export function useAuth() { const context useContext(AuthContext); if (!context) { throw new Error(useAuth must be used within AuthProvider); } return context; } // 使用 function LoginButton() { const { login, isAuthenticated, logout } useAuth(); if (isAuthenticated) { return button onClick{logout}退出/button; } return button onClick{() login(userexample.com, password)}登录/button; }3.3 Context useReducer// 定义状态和 reducer const initialState { user: null, theme: light, notifications: [], sidebarOpen: true }; function appReducer(state, action) { switch (action.type) { case SET_USER: return { ...state, user: action.payload }; case TOGGLE_THEME: return { ...state, theme: state.theme light ? dark : light }; case ADD_NOTIFICATION: return { ...state, notifications: [...state.notifications, action.payload] }; case TOGGLE_SIDEBAR: return { ...state, sidebarOpen: !state.sidebarOpen }; default: return state; } } // 创建 Context const AppContext React.createContext(); // Provider export function AppProvider({ children }) { const [state, dispatch] useReducer(appReducer, initialState); const value { state, dispatch }; return AppContext.Provider value{value}{children}/AppContext.Provider; } // 自定义 Hook export function useAppContext() { const context useContext(AppContext); if (!context) { throw new Error(useAppContext must be used within AppProvider); } return context; } // 使用 function ThemeToggle() { const { state, dispatch } useAppContext(); return ( button onClick{() dispatch({ type: TOGGLE_THEME })} 当前主题: {state.theme} /button ); }四、性能优化4.1 使用 useMemo 避免不必要的重渲染function ThemeProvider({ children }) { const [theme, setTheme] useState(light); // ✅ 使用 useMemo 缓存 value const value useMemo(() ({ theme, setTheme }), [theme]); return ( ThemeContext.Provider value{value} {children} /ThemeContext.Provider ); }4.2 拆分 Context// ❌ 不好的做法所有状态放在一个 Context 中 const AppContext React.createContext(); // 任何状态变化都会导致所有消费者重渲染 // ✅ 好的做法按职责拆分 Context const UserContext React.createContext(); const ThemeContext React.createContext(); const NotificationContext React.createContext(); function App() { const [user, setUser] useState(null); const [theme, setTheme] useState(light); const [notifications, setNotifications] useState([]); return ( UserContext.Provider value{{ user, setUser }} ThemeContext.Provider value{{ theme, setTheme }} NotificationContext.Provider value{{ notifications, setNotifications }} MainContent / /NotificationContext.Provider /ThemeContext.Provider /UserContext.Provider ); }4.3 使用 React.memo 优化消费者const ExpensiveComponent React.memo(({ data }) { // 复杂渲染逻辑 return div{/* ... */}/div; }); function Parent() { const { user } useContext(UserContext); // ExpensiveComponent 只在 user 变化时重渲染 return ExpensiveComponent data{user} /; }五、实战案例5.1 主题切换系统const ThemeContext React.createContext(); const themes { light: { background: #ffffff, color: #333333, primary: #007bff }, dark: { background: #1a1a1a, color: #ffffff, primary: #4dabf7 } }; export function ThemeProvider({ children }) { const [themeName, setThemeName] useState(light); const theme themes[themeName]; const toggleTheme () { setThemeName(prev prev light ? dark : light); }; const value useMemo(() ({ theme, themeName, toggleTheme }), [theme, themeName]); return ( ThemeContext.Provider value{value} {children} /ThemeContext.Provider ); } export function useTheme() { const context useContext(ThemeContext); if (!context) { throw new Error(useTheme must be used within ThemeProvider); } return context; } // 使用 function ThemedComponent() { const { theme, toggleTheme } useTheme(); return ( div style{{ background: theme.background, color: theme.color }} button onClick{toggleTheme}切换主题/button p当前主题/p /div ); }5.2 多语言系统const LanguageContext React.createContext(); const translations { zh-CN: { welcome: 欢迎, logout: 退出, settings: 设置 }, en-US: { welcome: Welcome, logout: Logout, settings: Settings } }; export function LanguageProvider({ children }) { const [locale, setLocale] useState(zh-CN); const t translations[locale]; const value useMemo(() ({ locale, setLocale, t, tKey: (key) t[key] || key }), [locale, t]); return ( LanguageContext.Provider value{value} {children} /LanguageContext.Provider ); } export function useLanguage() { const context useContext(LanguageContext); if (!context) { throw new Error(useLanguage must be used within LanguageProvider); } return context; } // 使用 function Header() { const { t, locale, setLocale } useLanguage(); return ( header h1{t.welcome}/h1 select value{locale} onChange{(e) setLocale(e.target.value)} option valuezh-CN中文/option option valueen-USEnglish/option /select /header ); }5.3 购物车状态管理const CartContext React.createContext(); function cartReducer(state, action) { switch (action.type) { case ADD_ITEM: { const existing state.items.find(item item.id action.payload.id); if (existing) { return { ...state, items: state.items.map(item item.id action.payload.id ? { ...item, quantity: item.quantity 1 } : item ) }; } return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }] }; } case REMOVE_ITEM: return { ...state, items: state.items.filter(item item.id ! action.payload) }; case UPDATE_QUANTITY: return { ...state, items: state.items.map(item item.id action.payload.id ? { ...item, quantity: Math.max(0, action.payload.quantity) } : item ).filter(item item.quantity 0) }; case CLEAR_CART: return { ...state, items: [] }; default: return state; } } export function CartProvider({ children }) { const [state, dispatch] useReducer(cartReducer, { items: [] }); const addItem (item) dispatch({ type: ADD_ITEM, payload: item }); const removeItem (id) dispatch({ type: REMOVE_ITEM, payload: id }); const updateQuantity (id, quantity) dispatch({ type: UPDATE_QUANTITY, payload: { id, quantity } }); const clearCart () dispatch({ type: CLEAR_CART }); const totalItems state.items.reduce((sum, item) sum item.quantity, 0); const totalPrice state.items.reduce( (sum, item) sum item.price * item.quantity, 0 ); const value { cart: state.items, totalItems, totalPrice, addItem, removeItem, updateQuantity, clearCart }; return ( CartContext.Provider value{value} {children} /CartContext.Provider ); } export function useCart() { const context useContext(CartContext); if (!context) { throw new Error(useCart must be used within CartProvider); } return context; }六、常见陷阱6.1 Provider 缺失导致错误// ❌ 忘记包裹 Provider function App() { return ComponentThatUsesContext /; // 会使用默认值或报错 } // ✅ 确保 Provider 存在 function App() { return ( ThemeProvider ComponentThatUsesContext / /ThemeProvider ); }6.2 不必要的重渲染// ❌ 每次渲染都创建新对象 function BadProvider({ children }) { const [count, setCount] useState(0); return ( MyContext.Provider value{{ count, setCount }} {children} /MyContext.Provider ); } // ✅ 使用 useMemo function GoodProvider({ children }) { const [count, setCount] useState(0); const value useMemo(() ({ count, setCount }), [count]); return ( MyContext.Provider value{value} {children} /MyContext.Provider ); }七、练习题基础题实现一个主题切换功能包含亮色/暗色两种主题实现一个用户登录状态管理显示登录用户信息进阶题实现一个多语言切换系统实现一个全局通知系统任何组件都可以发送通知参考答案// 通知系统 const NotificationContext React.createContext(); function NotificationProvider({ children }) { const [notifications, setNotifications] useState([]); const addNotification (message, type info) { const id Date.now(); setNotifications(prev [...prev, { id, message, type }]); setTimeout(() { setNotifications(prev prev.filter(n n.id ! id)); }, 3000); }; const removeNotification (id) { setNotifications(prev prev.filter(n n.id ! id)); }; return ( NotificationContext.Provider value{{ notifications, addNotification, removeNotification }} {children} NotificationList / /NotificationContext.Provider ); } function NotificationList() { const { notifications, removeNotification } useContext(NotificationContext); return ( div classNamenotifications {notifications.map(notif ( div key{notif.id} className{notification ${notif.type}} {notif.message} button onClick{() removeNotification(notif.id)}×/button /div ))} /div ); }八、小结要点说明适用场景主题、用户认证、语言等全局状态性能优化拆分 Context、使用 useMemo自定义 Hook封装 useContext 使用默认值仅在无 Provider 时生效核心要点Context 解决 props drilling 问题不要过度使用 Context配合 useReducer 管理复杂状态注意性能影响适当拆分