0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 25 关 含代码 约 10 分钟

使用 Context 深层传递参数:告别 Prop 逐级透传

使用 Context 深层传递参数:告别 Prop 逐级透传

通常情况下,我们通过 Props 把数据从父组件传给子组件。这在层级较浅时非常清晰自然。

但是想象一下这个场景: 在你的整个应用顶层,有一个表示当前“主题颜色(深色模式/浅色模式)”的状态。而页面深处的一个微小按钮需要读取这个颜色。 为了把这个变量送到底层按钮,你必须强迫中间所有的组件(App ightarrow ightarrow Layout ightarrow ightarrow Sidebar ightarrow ightarrow Menu ightarrow ightarrow MenuItem ightarrow ightarrow Button)都加上一个 theme prop,并像接力棒一样一级一级机械地传下去。

这种被迫穿透大量中间无辜组件的传参方式,被称为 Prop 逐级透传(Prop Drilling)。

为了彻底消灭这种繁琐痛苦的写法,React 提供了 Context(上下文)。它就像在顶层组件与底层子节点之间架设了一座 超时空传送门!


1. 使用 Context 的标准三部曲

无论多复杂的 Context 应用,都遵循以下清晰的三部曲:

第一步:创建 Context(Create the context)

使用 createContext 创建并导出一个 Context 对象。可以给它一个默认值:

// LevelContext.js
import { createContext } from 'react';

// 初始默认层级为 1
export const LevelContext = createContext(1);

第二步:在需要数据的深层子组件中使用 Context

在需要读取数据的任何子组件内部,引入 useContext Hook,直接把对应的 Context 传给它:

// Heading.js
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  // 魔法时刻:直接从传送门获取上层提供的数据!
  const level = useContext(LevelContext);

  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    default:
      throw Error('未知的层级: ' + level);
  }
}

请注意看!Heading 组件完全不需要从自己的 Props 里面接收 level!


第三步:在上层父组件中提供 Context(Provide the context)

在需要向下方子树广播数据的上层组件中,使用 LevelContext.Provider 把子组件包裹起来,并通过 value prop 传入要广播的值:

// Section.js
import { LevelContext } from './LevelContext.js';

export default function Section({ level, children }) {
  return (
    <section className="section">
      <LevelContext.Provider value={level}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}

2. 精彩的实战效果:嵌套标题

现在,使用这两个组件构建复杂的排版文章:

import Section from './Section.js';
import Heading from './Heading.js';

export default function Page() {
  return (
    <Section level={1}>
      <Heading>主标题(自动渲染为 H1)</Heading>
      <Section level={2}>
        <Heading>副标题(自动渲染为 H2)</Heading>
        <Heading>同级副标题(自动渲染为 H2)</Heading>
        <Section level={3}>
          <Heading>子章节(自动渲染为 H3)</Heading>
        </Section>
      </Section>
    </Section>
  );
}

所有的 Heading 都极其干净利落,不需要传任何层级参数,它们会顺着组件树自动寻找距离自己最近的那个 Provider,并读取对应的数值!


3. Context 的经典使用场景

什么时候应该使用 Context?

  • 全局主题(Theming):比如切换日间模式(Light Mode)与夜间模式(Dark Mode);
  • 当前登录用户(Current Account):许多深层组件需要展示当前登录者的头像与权限;
  • 路由状态(Routing):路由库利用 Context 让深层链接知道当前正处于哪个 URL 路径;
  • 语言国际化(Localization):多语言切换。

警惕:切勿滥用 Context! 如果只是两三层组件之间的普通数据流动,依然优先使用清晰直接的 Props。如果只是为了避免传参,优先考虑是否可以把子组件作为 children 传递(组件组合模式)。


总结

  • Context 解决了复杂层级深处组件访问全局或高层数据的“Prop 逐级透传”问题;
  • 三部曲:createContext 创建、useContext 读取、<Context.Provider value={...}> 广播;
  • 子组件总是从离它最近的同名 Provider 中读取数据;
  • 适合主题、登录凭证、语言等跨越整个子树的共享数据。
下一站:结合 Reducer 与 Context:构建清晰可扩展的全局数据层 →
© 2026 React 官方教程