1. Context 是什么?
Context 提供了一种在组件树中共享数据的方式,无需通过 props 手动逐层传递。它主要用于共享被视为"全局"的数据。
2. Context 解决的问题
解决了 "prop drilling"(属性钻取)问题,即:避免将 props 通过多层组件传递。
3. 基本使用方法
3.1 创建 Context
// ThemeContext.js
import { createContext } from 'react';
export const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {},
});
3.2 提供 Context
// App.js
import { ThemeContext } from './ThemeContext';
import { useState } from 'react';
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<MainContent />
</ThemeContext.Provider>
);
}
3.3 消费 Context
// 方式1:使用 useContext Hook
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
style={{ background: theme === 'light' ? '#fff' : '#000' }}
onClick={toggleTheme}
>
Current theme: {theme}
</button>
);
}
// 方式2:使用 Consumer
function ThemedText() {
return (
<ThemeContext.Consumer>
{({ theme }) => (
<p style={{ color: theme === 'light' ? '#000' : '#fff' }}>
This text uses the current theme
</p>
)}
</ThemeContext.Consumer>
);
}
4. 高级用法
4.1 组合多个 Context
function App() {
return (
<ThemeContext.Provider value={themeValue}>
<UserContext.Provider value={userValue}>
<MainContent />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
4.2 创建自定义 Hook
// useTheme.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
4.3 动态 Context
function DynamicThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const [themeConfig, setThemeConfig] = useState({
light: {
background: '#fff',
color: '#000'
},
dark: {
background: '#000',
color: '#fff'
}
});
const value = {
theme,
themeConfig: themeConfig[theme],
setTheme,
setThemeConfig
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
5. 完整案例
5.1 函数组件
// 1. 创建 Context
import React, { createContext, useContext, useState } from 'react';
const UserContext = createContext();
// 2. 创建 Provider 组件
export function UserProvider({ children }) {
const [user, setUser] = useState({
name: "John",
theme: "dark"
});
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
// 3. 创建自定义 Hook 来使用 Context
export function useUser() {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
}
// 4. 在组件中使用
function DeepNestedComponent() {
const { user, setUser } = useUser();
return (
<div>
<h1>Hello, {user.name}!</h1>
<button onClick={() => setUser({ ...user, theme: user.theme === 'dark' ? 'light' : 'dark' })}>
Toggle Theme
</button>
</div>
);
}
// 5. 在应用中包裹 Provider
function App() {
return (
<UserProvider>
<DeepNestedComponent />
</UserProvider>
);
}
6. 最佳实践
-
适当的使用场景:
- 全局主题配置
- 用户认证信息
- 语言偏好
- Redux/Mobx 等状态管理的替代方案(小型应用)
-
避免过度使用:
- Context 的变更会导致所有消费该 Context 的组件重新渲染
- 不要用 Context 来传递频繁变化的值
-
性能优化:
function OptimizedProvider({ children }) {
const [state, setState] = useState({
theme: 'light',
user: null
});
// 将方法用 useCallback 包裹,避免不必要的重渲染
const setTheme = useCallback((theme) => {
setState(prev => ({ ...prev, theme }));
}, []);
const value = useMemo(() => ({
...state,
setTheme
}), [state, setTheme]);
return (
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
);
}
7. 常见陷阱和注意事项
- 默认值陷阱:
// 错误示例
const MyContext = createContext();
// 正确示例
const MyContext = createContext(null);
// 或者提供一个默认值
const MyContext = createContext({ theme: 'light' });
- Provider 值变化:
// 错误示例 - 每次渲染都会创建新对象
function BadProvider({ children }) {
return (
<ThemeContext.Provider value={{ theme: 'light' }}>
{children}
</ThemeContext.Provider>
);
}
// 正确示例 - 使用 state 或 useMemo
function GoodProvider({ children }) {
const value = useMemo(() => ({
theme: 'light'
}), []);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
Context 是 React 中非常强大的特性,但需要谨慎使用。在适当的场景下使用 Context 可以大大简化代码结构,提高开发效率。但过度使用可能会导致组件复用性降低和性能问题。