React.createContext 与 useContext

#react

目录

1. 总结

  • React.createContextuseContext 需要配合使用
    • 一个定义
    • 一个使用
    • 示例
      • 图片&文件
  • 考虑是否真的需要 Context,有时 props 传递可能是更好的选择

2. 发布订阅模式

这就是一个 发布订阅模式

3. Context 的基本概念和使用场景

Context 提供了一种在组件树中共享数据的方式,无需通过 props 手动传递。主要用于:

// 常见的使用场景 // 1. 主题切换 // 2. 用户认证状态 // 3. 语言偏好 // 4. 全局配置

4. createContext 的基本使用

// 创建 Context const ThemeContext = React.createContext({ theme: 'light', toggleTheme: () => {} }); // 可以提供默认值 const UserContext = React.createContext(null); // Provider 组件 function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); const toggleTheme = useCallback(() => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); }, []); // value 属性传递共享值 return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } // 使用 Provider 包装应用 function App() { return ( <ThemeProvider> <MainContent /> </ThemeProvider> ); }

5. useContext 的使用方式

function ChildComponent() { // 使用 useContext 获取上下文值 const { theme, toggleTheme } = useContext(ThemeContext); return ( <div className={`theme-${theme}`}> <button onClick={toggleTheme}> Switch to {theme === 'light' ? 'dark' : 'light'} </button> </div> ); }

6. 高级用法和最佳实践

// 4.1 创建自定义 Hook 封装 Context 逻辑 function useTheme() { const context = useContext(ThemeContext); if (context === undefined) { throw new Error('useTheme must be used within a ThemeProvider'); } return context; } // 4.2 组合多个 Context function AppProviders({ children }) { return ( <ThemeProvider> <UserProvider> <SettingsProvider> {children} </SettingsProvider> </UserProvider> </ThemeProvider> ); } // 4.3 使用 Context 的 Consumer 组件(类组件中) class ClassComponent extends React.Component { render() { return ( <ThemeContext.Consumer> {value => ( <div className={`theme-${value.theme}`}> <button onClick={value.toggleTheme}> Toggle Theme </button> </div> )} </ThemeContext.Consumer> ); } }

7. 性能优化

// 5.1 使用 memo 优化子组件 const MemoizedChild = React.memo(function Child() { console.log("Child render"); const { theme } = useContext(ThemeContext); return <div>Theme: {theme}</div>; }); // 5.2 分离 Context 值 const ThemeContext = React.createContext('light'); const ThemeUpdateContext = React.createContext(() => {}); function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); const toggleTheme = useCallback(() => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); }, []); return ( <ThemeContext.Provider value={theme}> <ThemeUpdateContext.Provider value={toggleTheme}> {children} </ThemeUpdateContext.Provider> </ThemeContext.Provider> ); }

8. 实际应用示例

  • 全局状态管理
  • 多语言支持
// 6.1 全局状态管理 const GlobalStateContext = React.createContext(); function GlobalStateProvider({ children }) { const [state, dispatch] = useReducer(reducer, initialState); return ( <GlobalStateContext.Provider value={{ state, dispatch }}> {children} </GlobalStateContext.Provider> ); } // 6.2 多语言支持 const LanguageContext = React.createContext({ language: 'en', translations: {}, setLanguage: () => {} }); function useTranslation() { const { language, translations } = useContext(LanguageContext); const t = useCallback((key) => { return translations[language]?.[key] || key; }, [language, translations]); return { t, language }; }

9. 常见陷阱和注意事项

// 7.1 避免重复渲染 function ParentComponent() { const [count, setCount] = useState(0); // 不好的做法:每次渲染都创建新对象 return ( <ThemeContext.Provider value={{ theme: 'light', count }}> <ChildComponent /> </ThemeContext.Provider> ); // 好的做法:使用 useMemo const value = useMemo(() => ({ theme: 'light', count }), [count]); return ( <ThemeContext.Provider value={value}> <ChildComponent /> </ThemeContext.Provider> ); } // 7.2 Context 嵌套过深的问题 // 不好的做法 function DeepNesting() { return ( <ContextA.Provider> <ContextB.Provider> <ContextC.Provider> <ContextD.Provider> <Component /> </ContextD.Provider> </ContextC.Provider> </ContextB.Provider> </ContextA.Provider> ); } // 好的做法:使用组合 function CombinedProvider({ children }) { // 在一个组件中处理所有 context 逻辑 return ( <AppProviders> {children} </AppProviders> ); }

9.1. 避免过度使用

// 不推荐 const UserContext = React.createContext(); const ThemeContext = React.createContext(); const SettingsContext = React.createContext(); function DeepNestedComponent() { const user = useContext(UserContext); const theme = useContext(ThemeContext); const settings = useContext(SettingsContext); // ... } // 推荐 const AppContext = React.createContext(); function DeepNestedComponent() { const { user, theme, settings } = useContext(AppContext); // ... }

10. 调试技巧

// 8.1 添加 displayName ThemeContext.displayName = 'ThemeContext'; // 8.2 开发环境下的调试日志 function useDebugContext(context, name) { const value = useContext(context); useEffect(() => { if (process.env.NODE_ENV === 'development') { console.log(`${name} context value:`, value); } }, [value, name]); return value; }

11. 使用 Context 时的关键建议

  1. 适度使用:不要为了避免 props 传递就过度使用 Context
    • 考虑是否真的需要 Context,有时 props 传递可能是更好的选择
  2. 合理拆分:
    1. 将不同领域的 Context 分开,避免不必要的重渲染
  3. 提供默认值:
    1. 总是为 createContext 提供合理的默认值
  4. 错误处理:
    1. 在自定义 Hook 中添加必要的错误检查
  5. 性能优化:
    1. 使用 memo、useMemo 和 useCallback 优化性能
  6. 类型安全:
    1. 在 TypeScript 项目中,为 Context 添加适当的类型定义