React Context 使用详解
React Context 提供了一种在组件树中共享数据的方式,无需通过逐层手动传递 props,特别适合全局数据(如主题、用户认证等)的共享。
一、Context 基本概念
1. 什么是 Context?
- 解决组件多层嵌套传递 props的问题
- 实现跨组件层级的数据共享
- 适合全局数据(如用户信息、UI主题、语言偏好等)
2. 使用场景
- ✅ 主题切换(深色/浅色模式)
- ✅ 用户认证信息
- ✅ 本地化语言
- ✅ 全局缓存数据
- ❌ 组件间简单通信(优先考虑 props 或状态提升)
二、Context 核心 API
1. React.createContext
jsx
const MyContext = React.createContext(defaultValue);
- 创建一个 Context 对象
defaultValue仅在组件在树中没有匹配的 Provider时使用
2. Context.Provider
jsx
<MyContext.Provider value={/* 共享的值 */}>
{/* 子组件 */}
</MyContext.Provider>
- 允许消费组件订阅 context 的变化
- 接收
valueprop 传递给消费组件
3. Context.Consumer
jsx
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染 */}
</MyContext.Consumer>
- 类组件中订阅 context 的另一种方式
4. useContext Hook
jsx
const value = useContext(MyContext);
- 函数组件中订阅 context 的推荐方式
三、基础使用示例
1. 创建 Context
jsx
// contexts/theme.js
import { createContext } from 'react';
export const ThemeContext = createContext('light'); // 默认值 light
2. 提供 Context
jsx
// App.js
import { ThemeContext } from './contexts/theme';
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
3. 消费 Context(类组件)
jsx
class ThemedButton extends React.Component {
static contextType = ThemeContext; // 方式1:类组件专用
render() {
const { theme, setTheme } = this.context; // 获取 context
return (
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}
>
Toggle Theme
</button>
);
}
}
// 或者使用 Consumer(适用于复杂逻辑)
function ThemedButton() {
return (
<ThemeContext.Consumer>
{({ theme, setTheme }) => (
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}
>
Toggle Theme
</button>
)}
</ThemeContext.Consumer>
);
}
4. 消费 Context(函数组件)
jsx
import { useContext } from 'react';
import { ThemeContext } from './contexts/theme';
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}
>
Toggle Theme
</button>
);
}
四、高级用法
1. 多层嵌套 Context
jsx
// 多个 Context 嵌套
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
// 消费多个 Context
function Content() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div style={{ color: theme === 'dark' ? '#fff' : '#000' }}>
Welcome, {user.name}!
</div>
);
}
2. 动态 Context
jsx
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
const contextValue = {
theme,
setTheme,
user,
login: (userData) => setUser(userData),
logout: () => setUser(null)
};
return (
<AppContext.Provider value={contextValue}>
<Header />
<MainContent />
</AppContext.Provider>
);
}
3. 自定义 Provider 组件
jsx
// contexts/auth.js
export const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => setUser(userData);
const logout = () => setUser(null);
const value = { user, login, logout };
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// App.js
import { AuthProvider } from './contexts/auth';
function App() {
return (
<AuthProvider>
<Router>
{/* 路由配置 */}
</Router>
</AuthProvider>
);
}
4. 性能优化(避免不必要的渲染)
jsx
// 使用 useMemo 和 useCallback 优化
function App() {
const [theme, setTheme] = useState('light');
// 避免每次渲染都创建新的对象
const contextValue = useMemo(() => ({
theme,
toggleTheme: () => setTheme(t => t === 'light' ? 'dark' : 'light')
}), [theme]);
return (
<ThemeContext.Provider value={contextValue}>
<Toolbar />
</ThemeContext.Provider>
);
}
五、最佳实践
1. 组织 Context 文件
推荐目录结构:
src/
contexts/
ThemeContext.js
AuthContext.js
LocaleContext.js
index.js // 统一导出
2. 分离业务逻辑
jsx
// contexts/auth.js
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// 组件中使用
function UserProfile() {
const { user } = useAuth();
// ...
}
3. 类型安全(TypeScript)
typescript
interface ThemeContextType {
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark') => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
六、常见问题
1. Provider 未找到时使用默认值
jsx
const MyContext = createContext('default');
function Component() {
const value = useContext(MyContext); // 如果没有 Provider,value 为 'default'
// ...
}
2. 避免滥用 Context
- ❌ 不要用来替代所有组件通信
- ✅ 只用于真正需要跨多层级的全局数据
3. 性能问题解决方案
jsx
// 方案1:拆分 Context
// 不要把所有状态放在一个 Context 中
<UserContext.Provider value={user}>
<ThemeContext.Provider value={theme}>
{/* ... */}
</ThemeContext.Provider>
</UserContext.Provider>
// 方案2:使用 useMemo 记忆化值
const contextValue = useMemo(() => ({ user, setUser }), [user]);
七、完整示例(主题切换)
1. 创建 Context
jsx
// contexts/theme.js
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
2. 提供 Context
jsx
// App.js
import { ThemeProvider } from './contexts/theme';
function App() {
return (
<ThemeProvider>
<Header />
<MainContent />
<Footer />
</ThemeProvider>
);
}
3. 消费 Context
jsx
// components/ThemeToggle.js
import { useTheme } from '../contexts/theme';
function ThemeToggle() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current Theme: {theme} (Click to toggle)
</button>
);
}
总结
React Context 的核心使用流程:
- 创建 Context:
createContext() - 提供数据:
<Context.Provider value={value}> - 消费数据:
- 类组件:
static contextType或<Context.Consumer> - 函数组件:
useContext()
- 类组件:
最佳实践:
- 为不同的全局数据创建多个 Context
- 提供自定义 Hook(如
useTheme)简化消费 - 对动态 Context 值进行性能优化
- 在 TypeScript 中确保类型安全
Context 是 React 强大的状态管理工具,合理使用可以大幅简化深层嵌套组件的数据传递问题。
