React - useContext和深层传递参数

#题引:我认为跟着官方文档学习不会走歪路

通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递 props,或是在你应用中的许多组件需要相同的信息,传递 props 会变的十分冗长和不便。Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递。

假设Heading 组件接收一个level参数来决定它标题尺寸的场景

复制代码
export default function Page() {
  return (
    <Section>
      <Heading level={1}>主标题</Heading>
      <Section>
        <Heading level={2}>副标题</Heading>
        <Heading level={2}>副标题</Heading>
        <Heading level={2}>副标题</Heading>
        <Section>
          <Heading level={3}>子标题</Heading>
          <Heading level={3}>子标题</Heading>
          <Heading level={3}>子标题</Heading>
          <Section>
            <Heading level={4}>子子标题</Heading>
            <Heading level={4}>子子标题</Heading>
            <Heading level={4}>子子标题</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}


export default function Section({ children }) {
  return (
    <section className="section">
      {children}
    </section>
  );
}



export default function Heading({ level, children }) {
  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>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      throw Error('未知的 level:' + level);
  }
}

这就是 context 大显身手的地方

Step 1:创建 context

创建这个 context,并 将其从一个文件中导出,这样你的组件才可以使用它

LevelContext.js

复制代码
import { createContext } from 'react';

export const LevelContext = createContext(1);

createContext 只需默认值这么一个参数。在这里, 1 表示最大的标题级别,但是你可以传递任何类型的值(甚至可以传入一个对象)

Step 2:使用 Context

从 React 中引入 useContext Hook 以及你刚刚创建的 context

复制代码
export default function Heading({ children }) {
  const level = useContext(LevelContext);
  // ...
}

useContext 告诉 React Heading 组件想要读取 LevelContext

Step 3:提供 context

Section 组件目前渲染传入它的子组件,把它们用 context provider 包裹起来 以提供 LevelContext 给它们

复制代码
import { LevelContext } from './LevelContext.js';

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

这告诉 React:"如果在 Section组件中的任何子组件请求 LevelContext,给他们这个 level。"组件会使用 UI 树中在它上层最近的那个 <LevelContext.Provider> 传递过来的值。

由于 context 让你可以从上层的组件读取信息,每个 Section 都会从上层的 Section 读取 level,并自动向下层传递 level + 1。

你可以像下面这样做:

复制代码
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

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

这样修改之后,你不用将 level 参数传给 Section>或者是 Heading

复制代码
export default function Page() {
  return (
    <Section>
      <Heading>主标题</Heading>
      <Section>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Section>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Section>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}

在 React 中,覆盖来自上层的某些 context 的唯一方法是将子组件包裹到一个提供不同值的 context provider 中

Context 的使用场景
  • 主题: 如果你的应用允许用户更改其外观
  • 当前账户: 许多组件可能需要知道当前登录的用户信息
  • 路由: 大多数路由解决方案在其内部使用 context 来保存当前路由
  • 状态管理: 随着你的应用的增长,最终在靠近应用顶部的位置可能会有很多 state。许多遥远的下层组件可能想要修改它们。通常 将 reducer 与 context 搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦。

Context 不局限于静态值。如果你在下一次渲染时传递不同的值,React 将会更新读取它的所有下层组件。

useContext与与useState结合

1: 创建 Context 和 Provider 组件:

使用 Provider 来包裹需要访问这个上下文的组件,并提供一个状态来管理上下文的值

复制代码
import React, { createContext, useState } from 'react';

const MyContext = createContext();
const MyProvider = ({ children }) => {
    const [value, setValue] = useState("初始值");

    return (
        <MyContext.Provider value={{ value, setValue }}>
            {children}
        </MyContext.Provider>
    );
};

2: 使用 useContext:

在子组件中使用 useContext 来访问上下文的值和变更函数

复制代码
import React, { useContext } from 'react';

const MyComponent = () => {
    const { value, setValue } = useContext(MyContext);

    const changeValue = () => {
        setValue("新值");
    };

    return (
        <div>
            <p>当前值: {value}</p>
            <button onClick={changeValue}>变更值</button>
        </div>
    );
};

3: 包裹应用

复制代码
const App = () => {
    return (
        <MyProvider>
            <MyComponent />
        </MyProvider>
    );
};
useContext与与useReducer结合

1: 定义 Reducer 函数

复制代码
const initialState = { count: 0 };

const reducer = (state, action) => {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
};

2:创建 Context 和 Provider 组件

使用 useReducer 在 Provider 中管理状态,并将状态和 dispatch 方法提供给上下文

复制代码
import React, { createContext, useReducer } from 'react';

const MyContext = createContext();

const MyProvider = ({ children }) => {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <MyContext.Provider value={{ state, dispatch }}>
            {children}
        </MyContext.Provider>
    );
};

3:使用 Context 和 Reducer

在子组件中使用 useContext 来访问状态和 dispatch 方法

复制代码
const Counter = () => {
    const { state, dispatch } = useContext(MyContext);

    return (
        <div>
            <p>计数: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>增加</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
        </div>
    );
};

4:包裹应用

复制代码
const App = () => {
    return (
        <MyProvider>
            <MyComponent />
        </MyProvider>
    );
};
相关推荐
吃手机用谁付的款20 分钟前
HTML常见标签
前端·html
好好研究28 分钟前
CSS样式中的布局、字体、响应式布局
前端·css
拉不动的猪2 小时前
前端小白之 CSS弹性布局基础使用规范案例讲解
前端·javascript·css
伍哥的传说2 小时前
React强大且灵活hooks库——ahooks入门实践之开发调试类hook(dev)详解
前端·javascript·react.js·ecmascript·hooks·react-hooks·ahooks
界面开发小八哥2 小时前
界面控件Kendo UI for Angular 2025 Q2新版亮点 - 增强跨设备的无缝体验
前端·ui·界面控件·kendo ui·angular.js
枷锁—sha3 小时前
从零掌握XML与DTD实体:原理、XXE漏洞攻防
xml·前端·网络·chrome·web安全·网络安全
F2E_Zhangmo3 小时前
基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构
前端·javascript·vue·cornerstone3d·cornerstonejs
代码的余温4 小时前
如何区别HTML和HTML5?
前端·html·html5
天下无贼!4 小时前
【样式效果】纯CSS从零到一实现动态彩色背景效果
前端·css
DoraBigHead4 小时前
手写 `new`、`call`、`apply`、`bind` + V8 函数调用机制解密
前端·javascript·面试