React.createContext 与 useContext
#react
目录
- 1. 总结
- 2. 发布订阅模式
- 3. Context 的基本概念和使用场景
- 4. createContext 的基本使用
- 5. useContext 的使用方式
- 6. 高级用法和最佳实践
- 7. 性能优化
- 8. 实际应用示例
- 9. 常见陷阱和注意事项
- 10. 调试技巧
- 11. 使用 Context 时的关键建议
1. 总结
React.createContext
与useContext
需要==配合使用==- 一个定义
- 一个使用
- 示例
- 考虑是否真的需要 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 时的关键建议
- 适度使用:不要为了避免 props 传递就过度使用 Context
- 考虑是否真的需要 Context,有时 props 传递可能是更好的选择
- 合理拆分:
- 将不同领域的 Context 分开,避免不必要的重渲染
- 提供默认值:
- 总是为 createContext 提供合理的默认值
- 错误处理:
- 在自定义 Hook 中添加必要的错误检查
- 性能优化:
- 使用 memo、useMemo 和 useCallback 优化性能
- 类型安全:
- 在 TypeScript 项目中,为 Context 添加适当的类型定义