Context 深入
Context 很强大:它能让你“跨层级”传值,避免 props 一层层往下传。但它也很容易被滥用,并且会影响渲染范围。 这一章专门讲 Context 的进阶用法与性能注意事项。
复习:最小 Context
TypeScript
import { createContext, useContext } from 'react';
type Theme = 'light' | 'dark';
const ThemeContext = createContext<Theme>('light');
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme === 'dark' ? 'dark' : 'light'}>按钮</button>;
}
export default function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}性能关键点:Provider value 的引用稳定性
如果你把一个新对象直接作为 value 传入,那么每次渲染都会创建新引用,导致订阅者频繁渲染。
TypeScript
import { createContext, useContext, useState } from 'react';
type Auth = { userId: string | null; login: () => void };
const AuthContext = createContext<Auth | null>(null);
function Profile() {
const auth = useContext(AuthContext);
return <p>userId: {auth?.userId ?? 'anonymous'}</p>;
}
export default function App() {
const [userId, setUserId] = useState<string | null>(null);
const value = {
userId,
login() {
setUserId('u_123');
},
};
return (
<AuthContext.Provider value={value}>
<Profile />
</AuthContext.Provider>
);
}更好的做法是稳定函数引用,并用 useMemo 缓存 value:
TypeScript
import { createContext, useContext, useMemo, useState, useCallback } from 'react';
type Auth = { userId: string | null; login: () => void };
const AuthContext = createContext<Auth | null>(null);
function Profile() {
const auth = useContext(AuthContext);
return <p>userId: {auth?.userId ?? 'anonymous'}</p>;
}
export default function App() {
const [userId, setUserId] = useState<string | null>(null);
const login = useCallback(() => setUserId('u_123'), []);
const value = useMemo(() => ({ userId, login }), [userId, login]);
return (
<AuthContext.Provider value={value}>
<Profile />
</AuthContext.Provider>
);
}拆分 Context:降低重渲染范围
经验法则
- 一个 Context 里放太多字段,任何一个字段变化都会影响所有订阅者。
- 把“经常变的”和“很少变的”拆成多个 Context,能显著减少渲染波及。
什么时候不要用 Context?
- 只是父传子两三层:先尝试拆组件/提升 state/组合 children。
- 把组件内部的局部状态硬塞到 Context:会让依赖关系变得隐蔽。
- 需要复杂的派生、选择器、时间旅行:可能需要专用状态管理方案。
下一步
- 想从零理解 Context 的基础用法:Context。
- 想查清 API 契约与边界:useContext。
- 想了解全局状态的取舍与替代方案:状态管理。
- 想学习复用逻辑的方式:编写自己的 Hooks。
动手练习
运行并修改下面的组件,熟悉交互式示例的编辑与预览。
🌐浏览器运行