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>
    );
};
相关推荐
李子琪。2 分钟前
Web 漏洞与防御机制实验报告
前端·经验分享
JustNow_Man6 分钟前
“失败后自动拉起修复 Agent”的闭环流水线
前端·人工智能·chrome·python
Dxy12393102169 分钟前
HTML中如何写键盘事件
前端·html·计算机外设
霍格沃兹测试学院-小舟畅学11 分钟前
接口自动化测试的下一个十年:从脚本到Skills,让AI学会“如何测”
java·前端·人工智能
huangfuyk13 分钟前
前端使用Cursor编辑器方面遇到的问题及注意细节
前端·编辑器·ai编程·cursor
ZC跨境爬虫16 分钟前
跟着 MDN 学CSS day_31:(精通链接样式,从伪类到导航菜单)
前端·javascript·css·ui·交互
发现你走远了26 分钟前
前端多环境自动化部署实战:GitHub Actions + Azure Blob + Cloudflare
前端·自动化·github
香香爱编程26 分钟前
vue3自定义顶部弹窗
前端·javascript·vue.js
weelinking37 分钟前
【产品】10_搭建前端框架——把你的原型变成真实页面
java·大数据·前端·数据库·人工智能·python·前端框架
蜡台37 分钟前
Vue Echart 的 **高阶组件化** 封装思路
前端·javascript·vue.js·echarts