useContext
useContext是 React Hook,用于在函数组件中读取和订阅 Context 的值。
基本语法
JavaScript
import { useContext } from 'react';
const value = useContext(SomeContext);TypeScript
import { useContext } from 'react';
const value = useContext(SomeContext);
// TypeScript 会自动推断 value 的类型API 签名
function useContext<T>(context: React.Context<T>): T;参数:
context:通过createContext()创建的 Context 对象- 类型:
React.Context<T>,其中T是 Context 值的类型
返回值:
- 当前组件树中最近的
Context.Provider的value值 - 如果没有
Provider,返回createContext()时传入的默认值 - 类型:
T(Context 值的类型)
基本用法
示例 1:读取简单值
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Component />
</ThemeContext.Provider>
);
}
function Component() {
const theme = useContext(ThemeContext);
return <div className={theme}>Content</div>;
}示例 2:读取对象值
const UserContext = createContext({ name: 'Guest' });
function App() {
return (
<UserContext.Provider value={{ name: 'John' }}>
<Component />
</UserContext.Provider>
);
}
function Component() {
const user = useContext(UserContext);
return <div>Hello, {user.name}</div>;
}示例 3:解构对象值
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {}
});
function Component() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div className={theme}>
<button onClick={toggleTheme}>Toggle</button>
</div>
);
}值查找机制
向上查找最近的 Provider
function App() {
return (
<ThemeContext.Provider value="dark">
<Level1 />
</ThemeContext.Provider>
);
}
function Level1() {
return (
<ThemeContext.Provider value="light">
<Level2 />
</ThemeContext.Provider>
);
}
function Level2() {
// 使用最近的 Provider 的值:'light'
const theme = useContext(ThemeContext);
return <div>{theme}</div>;
}没有 Provider 时使用默认值
const ThemeContext = createContext('light');
// 没有 Provider
function App() {
return <Component />;
}
function Component() {
// 使用默认值:'light'
const theme = useContext(ThemeContext);
return <div className={theme}>Content</div>;
}更新机制
自动重新渲染
function App() {
const [count, setCount] = useState(0);
return (
<CountContext.Provider value={count}>
<Component />
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</CountContext.Provider>
);
}
function Component() {
// 当 count 改变时,组件会自动重新渲染
const count = useContext(CountContext);
return <div>Count: {count}</div>;
}重要:当 Provider 的 value 改变时,所有使用 useContext 的组件都会重新渲染。
多个 Context 使用
使用多个 useContext
function Component() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const language = useContext(LanguageContext);
return (
<div className={theme}>
<p>Hello, {user.name}</p>
<p>Language: {language}</p>
</div>
);
}封装成自定义 Hook
function useApp() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
const language = useContext(LanguageContext);
return { theme, user, language };
}
function Component() {
const { theme, user, language } = useApp();
return <div className={theme}>Content</div>;
}错误处理
检查 Context 是否存在
// 方式 1:使用默认值
function Component() {
const theme = useContext(ThemeContext) || 'light';
return <div className={theme}>Content</div>;
}
// 方式 2:抛出错误(推荐)
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
function Component() {
const theme = useTheme();
return <div className={theme}>Content</div>;
}TypeScript 使用
类型推断
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
function Component() {
const context = useContext(ThemeContext);
// TypeScript 知道 context 可能是 undefined
if (!context) {
throw new Error('Component must be used within ThemeProvider');
}
// TypeScript 知道 context 是 ThemeContextType
const { theme, toggleTheme } = context;
return <div className={theme}>Content</div>;
}自定义 Hook 类型
function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
function Component() {
// TypeScript 自动推断类型
const { theme, toggleTheme } = useTheme();
return <div className={theme}>Content</div>;
}完整示例
示例 1:主题切换
import { createContext, useContext, useState, useCallback, useMemo } from 'react';
const ThemeContext = createContext(undefined);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = useCallback(() => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
}, []);
const value = useMemo(() => ({
theme,
toggleTheme
}), [theme, toggleTheme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
function App() {
return (
<ThemeProvider>
<Component />
</ThemeProvider>
);
}
function Component() {
const { theme, toggleTheme } = useTheme();
return (
<div className={theme}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}常见错误
错误 1:在 Provider 外部使用
// ❌ 错误:在 Provider 外部使用
function App() {
return (
<>
<Component /> {/* 无法访问 Context */}
<ThemeProvider>
<Component /> {/* 可以访问 Context */}
</ThemeProvider>
</>
);
}
// ✅ 正确:在 Provider 内部使用
function App() {
return (
<ThemeProvider>
<Component /> {/* 可以访问 Context */}
</ThemeProvider>
);
}错误 2:忘记处理 undefined
// ❌ 错误:可能返回 undefined
function Component() {
const theme = useContext(ThemeContext); // 可能是 undefined
return <div className={theme}>Content</div>; // 错误
}
// ✅ 正确:处理 undefined
function Component() {
const theme = useContext(ThemeContext) || 'light';
return <div className={theme}>Content</div>;
}最佳实践
- 创建自定义 Hook:封装
useContext调用,提供错误处理 - 处理 undefined:检查 Context 是否存在
- 使用 TypeScript:提供类型安全
- 避免过度使用:只在需要跨组件传递数据时使用