Context 与依赖范围
Context 与依赖范围 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
主题、语言、学习偏好这类「很多组件都要、又懒得逐层传」的数据,正适合 Context。读完你应该能用 createContext + useContext 搭出主题切换,分得清什么时候该用 Context、什么时候属性传递更好,并会控制 Provider 的作用范围。
问题场景
设置面板里有个主题开关,偏好要传给三层之下的卡片组件。属性传递的写法是层层转发:
<Settings theme={theme} onToggle={...} />
// Settings 内部
<Card theme={theme} onToggle={...} />
// Card 内部
<ThemeButton theme={theme} onToggle={...} />
中间的 Settings 和 Card 自己根本不用 theme,却被迫接住再转手——这就是「属性逐层下钻」。任何一层重构签名,上下层全得跟着改。
核心解释
Context 让顶层声明、深处直接取用,中间层完全不用知情:
const ThemeContext = createContext("light");
function ThemeButton() {
const theme = useContext(ThemeContext); // 直接读,不用 props
return <button className={theme}>切换主题</button>;
}
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={theme}>
<Settings /> {/* 内部任何深度的组件都能 useContext 读到 theme */}
</ThemeContext.Provider>
);
}
两条使用纪律:
- Context 是「环境」,props 是「参数」。会随每次使用变化的、需要调用方显式给的值(一条数据、一个回调)走 props;整个子树共享的环境值(主题、当前用户、语言)走 Context。把课程列表这种「页面数据」塞进 Context,等于让所有组件都依赖它,复用和测试都会变难。
- Provider 有作用范围。
useContext读的是组件树上最近的上层 Provider——你可以在子树的某个分支再套一层 Provider,覆盖全局值。实验区里的局部深色面板就是这么实现的。
还要注意范围不要贪大:Provider 包得越大,value 变化时重渲染的面就越大。主题这类低频值问题不大;高频变化的值(输入框内容、鼠标位置)别放 Context。
示例拆解
实验区包含一个全局主题和一个局部覆盖:
- 顶部按钮切换全局
theme(light / dark),普通面板跟随变化——它 useContext 读到的是最外层 Provider; - 「局部面板」外面套了第二层
<ThemeContext.Provider value="dark">,所以无论全局怎么切,它始终是深色——最近 Provider 优先; - 对照点:面板标题、边框、背景全部来自 context 值,没有任何 theme prop 出现在组件签名里;
- 验证方式:连续切换全局主题两次,观察普通面板变色、局部面板纹丝不动,确认两条读取路径互不干扰。
动手任务
打开本课实验区,完成三个小改动:
- 给主题加第三档
sepia(米黄色背景),让全局按钮在 light / dark / sepia 间循环。 - 新增一个「局部浅色」面板:在全局 dark 时用内层 Provider 强制它保持 light。
- 把 ThemeContext 抽到单独文件(组件外
export const ThemeContext = createContext(...)),体验跨文件共享 context 的组织方式。
常见错误
- 在组件内部创建 context:每次渲染都会得到一个新 context 对象,其他组件 useContext 永远读不到值;createContext 必须在模块顶层调用。
- 忘包 Provider 直接 useContext:拿到的是 createContext 的默认值(比如 "light"),而且没有任何报错提醒你;要么提供 Provider,要么把默认值设计成有意义的兜底。
- 把高频状态塞进全局 Context:输入框每敲一个字全应用重渲染;这类值留在最近的组件里,或拆分 Provider 范围。
- 该用 props 的地方用了 Context:一个只被两处使用的回调绕道全局传递,调用关系反而看不清了;Context 解决的是「深层 + 广泛」的传递,不是所有传递。
检查与复盘
完成下方检查题确认理解。自查清单:
- 能说出 props 和 Context 的分工标准:显式参数 vs 子树环境
- 知道 useContext 读的是最近的上层 Provider,会用嵌套 Provider 做局部覆盖
- 会评估一个 Context 的 value 变化频率,决定要不要缩小 Provider 范围
参考资料
- React 官方文档:用 Context 深层传递数据 —— 什么时候该用、怎么组织 Provider
- React 官方文档:useContext API —— 查找规则与默认值行为
读完这一课了?
标记完成后,工作台会安排接下来的学习。