React 18 使用 Context 深层传递参数

这篇具有很好参考价值的文章主要介绍了React 18 使用 Context 深层传递参数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考文章

使用 Context 深层传递参数

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

传递 props 带来的问题

传递 props 是将数据通过 UI 树显式传递到使用它的组件的好方法。

但是当需要在组件树中深层传递参数以及需要在组件间复用相同的参数时,传递 props 就会变得很麻烦。最近的根节点父组件可能离需要数据的组件很远,状态提升 到太高的层级会导致 “逐层传递 props” 的情况。

React 18 使用 Context 深层传递参数,前端,react,Context,Context 的使用场景,传递 props 带来的问题,使用Context深层传递参数,创建 context

React 的 context 功能可以在组件树中不需要 props 将数据“直达”到所需的组件中。

Context:传递 props 的另一种方法

Context 让父组件可以为它下面的整个组件树提供数据。Context 有很多种用途。这里就有一个示例。思考一下这个 Heading 组件接收一个 level 参数来决定它标题尺寸的场景:

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

export default function Page() {
  return (
    <Section>
      <Heading level={1}>主标题</Heading>
      <Heading level={2}>副标题</Heading>
      <Heading level={3}>子标题</Heading>
	  <Heading level={4}>子子标题</Heading>
    </Section>
  );
}
// Section.js
export default function Section({ children }) {
  return (
    <section className="section">
      {children}
    </section>
  );
}
//  Heading.js
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>;
    default:
      throw Error('未知的 level:' + level);
  }
}

假设想让相同 Section 中的多个 Heading 具有相同的尺寸:

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

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

目前,将 level 参数分别传递给每个 <Heading>

<Section>
  <Heading level={3}>关于</Heading>
  <Heading level={3}>照片</Heading>
</Section>

level 参数传递给 <Section> 组件而不是传给 <Heading> 组件看起来更好一些。这样的话可以强制使同一个 section 中的所有标题都有相同的尺寸:

<Section level={3}>
  <Heading>关于</Heading>
  <Heading>照片</Heading>
</Section>

但是 <Heading> 组件是如何知道离它最近的 <Section> 的 level 的呢?

这需要子组件可以通过某种方式“访问”到组件树中某处在其上层的数据。

不能只通过 props 来实现它。这就是 context 大显身手的地方。可以通过以下三个步骤来实现它:

  1. 创建 一个 context。(可以将其命名为 LevelContext, 因为它表示的是标题级别。)
  2. 在需要数据的组件内 使用 刚刚创建的 context。(Heading 将会使用 LevelContext。)
  3. 在指定数据的组件中 提供 这个 context。 (Section 将会提供 LevelContext。)

Context 可以让父节点,甚至是很远的父节点都可以为其内部的整个组件树提供数据

React 18 使用 Context 深层传递参数,前端,react,Context,Context 的使用场景,传递 props 带来的问题,使用Context深层传递参数,创建 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:

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

目前,Heading 组件从 props 中读取 level

export default function Heading({ level, children }) {
  // ...
}

删掉 level 参数并从刚刚引入的 LevelContext 中读取值:

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

useContext 是一个 Hook。和 useState 以及 useReducer一样,只能在 React 组件中(不是循环或者条件里)立即调用 Hook。useContext 告诉 React Heading 组件想要读取 LevelContext

现在 Heading 组件没有 level 参数,不需要再像这样在 JSX 中将 level 参数传递给 Heading

<Section>
  <Heading level={4}>子子标题</Heading>
  <Heading level={4}>子子标题</Heading>
</Section>

修改一下 JSX,让 Section 组件代替 Heading 组件接收 level 参数:

<Section level={4}>
  <Heading>子子标题</Heading>
  <Heading>子子标题</Heading>
</Section>

将修改下边的代码直到它正常运行:

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

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

注意:这个示例还不能运行。所有 headings 的尺寸都一样,因为 即使正在使用 context,但是还没有提供它。 React 不知道从哪里获取这个 context!

如果不提供 context,React 会使用在上一步指定的默认值。在这个例子中,为 createContext 传入了 1 这个参数,所以 useContext(LevelContext) 会返回 1,把所有的标题都设置为<h1>。通过让每个 Section 提供它自己的 context 来修复这个问题。

Step 3:提供 context

Section 组件目前渲染传入它的子组件:

export default function Section({ children }) {
  return (
    <section className="section">
      {children}
    </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> 传递过来的值。

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

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

这与原始代码的运行结果相同,但是不需要向每个 Heading 组件传递 level 参数了!取而代之的是,它通过访问上层最近的 Section 来“断定”它的标题级别:

  1. 将一个 level 参数传递给 <Section>
  2. Section 把它的子元素包在 <LevelContext.Provider value={level}> 里面。
  3. Heading 使用 useContext(LevelContext) 访问上层最近的 LevelContext 提供的值。

在相同的组件中使用并提供 context

目前,仍需要手动指定每个 section 的 level

export default function Page() {
  return (
    <Section level={1}>
      ...
      <Section level={2}>
        ...
        <Section level={3}>
          ...

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

// Section.js
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> 了:

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

export default function Page() {
  return (
    <Section>
      <Heading>主标题</Heading>
      <Section>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Section>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Section>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}
// Heading.js
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
  const level = useContext(LevelContext);
  switch (level) {
    case 0:
      throw Error('Heading 必须在 Section 内部!');
    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:' + level);
  }
}

现在,HeadingSection 都通过读取 LevelContext 来判断它们的深度。而且 Section 把它的子组件都包在 LevelContext 中来指定其中的任何内容都处于一个“更深”的级别。

注意:本示例使用标题级别来展示,因为它们直观地显示了嵌套组件如何覆盖 context。但是 context 对于许多其他的场景也很有用。可以用它来传递整个子树需要的任何信息:当前的颜色主题、当前登录的用户等。

Context 会穿过中间层级的组件

可以在提供 context 的组件和使用它的组件之间的层级插入任意数量的组件。这包括像 <div> 这样的内置组件和自己创建的组件。

在这个示例中,相同的 Post 组件(带有虚线边框)在两个不同的嵌套层级上渲染。注意,它内部的 <Heading> 会自动从最近的 <Section> 获取它的级别:

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

export default function ProfilePage() {
  return (
    <Section>
      <Heading>My Profile</Heading>
      <Post
        title="旅行者,你好!"
        body="来看看我的冒险。"
      />
      <AllPosts />
    </Section>
  );
}

function AllPosts() {
  return (
    <Section>
      <Heading>帖子</Heading>
      <RecentPosts />
    </Section>
  );
}

function RecentPosts() {
  return (
    <Section>
      <Heading>最近的帖子</Heading>
      <Post
        title="里斯本的味道"
        body="...那些蛋挞!"
      />
      <Post
        title="探戈节奏中的布宜诺斯艾利斯"
        body="我爱它!"
      />
    </Section>
  );
}

function Post({ title, body }) {
  return (
    <Section isFancy={true}>
      <Heading>
        {title}
      </Heading>
      <p><i>{body}</i></p>
    </Section>
  );
}
// Section.js
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Section({ children, isFancy }) {
  const level = useContext(LevelContext);
  return (
    <section className={
      'section ' +
      (isFancy ? 'fancy' : '')
    }>
      <LevelContext.Provider value={level + 1}>
        {children}
      </LevelContext.Provider>
    </section>
  );
}

不需要做任何特殊的操作。Section 为它内部的树指定一个 context,所以可以在任何地方插入一个 <Heading>,而且它会有正确的尺寸。

Context 让你可以编写“适应周围环境”的组件,并且根据 在哪 (或者说 在哪个 context 中)来渲染它们不同的样子。

Context 的工作方式可能会让你想起 CSS 属性继承。在 CSS 中,可以为一个 <div> 手动指定 color: blue,并且其中的任何 DOM 节点,无论多深,都会继承那个颜色,除非中间的其他 DOM 节点用 color: green 来覆盖它。类似地,在 React 中,覆盖来自上层的某些 context 的唯一方法是将子组件包裹到一个提供不同值的 context provider 中

在 CSS 中,诸如 colorbackground-color 之类的不同属性不会覆盖彼此。可以设置所有 <div>color 为红色,而不会影响 background-color。类似地,不同的 React context 不会覆盖彼此。通过 createContext() 创建的每个 context 都和其他 context 完全分离,只有使用和提供 那个特定的 context 的组件才会联系在一起。一个组件可以轻松地使用或者提供许多不同的 context。

写在使用 context 之前

使用 Context 看起来非常诱人!然而,这也意味着它也太容易被过度使用了。如果只想把一些 props 传递到多个层级中,这并不意味着需要把这些信息放到 context 里。

在使用 context 之前,可以考虑以下几种替代方案:

  1. 从 传递 props 开始。 如果组件看起来不起眼,那么通过十几个组件向下传递一堆 props 并不罕见。这有点像是在埋头苦干,但是这样做可以让哪些组件用了哪些数据变得十分清晰!维护你代码的人会很高兴你用 props 让数据流变得更加清晰。
  2. 抽象组件并 将 JSX 作为 children 传递 给它们。 如果通过很多层不使用该数据的中间组件(并且只会向下传递)来传递数据,这通常意味着在此过程中忘记了抽象组件。举个例子,可能想传递一些像 posts 的数据 props 到不会直接使用这个参数的组件,类似 <Layout posts={posts} />。取而代之的是,让 Layoutchildren 当做一个参数,然后渲染 <Layout><Posts posts={posts} /></Layout>。这样就减少了定义数据的组件和使用数据的组件之间的层级。

如果这两种方法都不适合,再考虑使用 context。

Context 的使用场景

  • 主题: 如果应用允许用户更改其外观(例如暗夜模式),可以在应用顶层放一个 context provider,并在需要调整其外观的组件中使用该 context。
  • 当前账户: 许多组件可能需要知道当前登录的用户信息。将它放到 context 中可以方便地在树中的任何位置读取它。某些应用还允许同时操作多个账户(例如,以不同用户的身份发表评论)。在这些情况下,将 UI 的一部分包裹到具有不同账户数据的 provider 中会很方便。
  • 路由: 大多数路由解决方案在其内部使用 context 来保存当前路由。这就是每个链接“知道”它是否处于活动状态的方式。如果你创建自己的路由库,你可能也会这么做。
  • 状态管理: 随着应用的增长,最终在靠近应用顶部的位置可能会有很多 state。许多遥远的下层组件可能想要修改它们。通常 将 reducer 与 context 搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦。

Context 不局限于静态值。如果在下一次渲染时传递不同的值,React 将会更新读取它的所有下层组件!这就是 context 经常和 state 结合使用的原因。

一般而言,如果树中不同部分的远距离组件需要某些信息,context 将会对你大有帮助。文章来源地址https://www.toymoban.com/news/detail-698356.html

摘要

  • Context 使组件向其下方的整个树提供信息。
  • 传递 Context 的方法:
    1. 通过 export const MyContext = createContext(defaultValue) 创建并导出 context。
    2. 在无论层级多深的任何子组件中,把 context 传递给 useContext(MyContext) Hook 来读取它。
    3. 在父组件中把 children 包在 <MyContext.Provider value={...}> 中来提供 context。
  • Context 会穿过中间的任何组件。
  • Context 可以让你写出 “较为通用” 的组件。
  • 在使用 context 之前,先试试传递 props 或者将 JSX 作为 children 传递。

到了这里,关于React 18 使用 Context 深层传递参数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React中路由的参数传递 - 路由的配置文件

    路由的参数传递 传递参数有二种方式( 需要注意的是, 这两种方式在Router6.x中都是提供的hook函数的API, 类组件需要通过高阶组件的方式使用 ) : 动态路由的方式; search传递参数( 查询字符串 ); 方式一: 动态路由的概念指的是路由中的路径并不会固定 : 比如/detail的path对应一个组件

    2023年04月08日
    浏览(34)
  • React-Router 5.0 制作导航栏+页面参数传递

    使用 React 构建 SPA 应用(单页面应用),要想实现页面间的跳转,首先想到的就是使用路由。在 React 中,常用的有两个包可以实现这个需求,那就是 react-router 和 react-router-dom 。本文主要针对 react-router-dom 进行说明。 众所周知, JS 由 DOM BOM ECMAScript 组成, React-Router-Dom 使用 BOM 提供

    2024年02月08日
    浏览(45)
  • react自定义组件间的传值,if..else..判断,for循环(嵌套map使用),点击事件(Onclick),页面上事件实事传递参数(基础版)

    9.自定义组件间的传值 10.if..else..判断 11.for循环(嵌套map使用) 12.点击事件(Onclick) 13.页面上事件实事传递参数 14.关于export default function App()与export function App()的区别    export default不需要{}      import  Gallery from \\\'./Gallery.js\\\';    export function需要{}       import {Gallery} from \\\'./Galler

    2024年01月17日
    浏览(43)
  • React 中的 context使用方法

    什么是上下文? 全局变量就是全局的上下文,全局都可以访问到它;上下文就是你运行一段代码,所要知道的所有变量。 三大件 React.createContext(): context 的初始化 provider: 提供context的父组件,用来包裹需要context的子组件的 子组件获得context的方法 代码如5:

    2024年02月15日
    浏览(38)
  • React - 你知道props和state之间深层次的区别吗

    难度级别:初级及以上                                 提问概率:60%  如果把React组件看做一个函数的话,props更像是外部传入的参数,而state更像是函数内部定义的变量。那么他们还有哪些更深层次的区别呢,我们来看一下。 首先说props,他是组件外部传入

    2024年04月13日
    浏览(32)
  • React 18中新钩子 useDeferredValue 使用

    React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是 useDeferredValue 钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue 钩子是React性能优化工具集中相对较新的补充。 它在处理异步数

    2024年01月19日
    浏览(39)
  • 使用React18+Ts创建项目

    首先,使用create-react-app工具创建一个新的React项目: 使用脚手架创建项目后,自带react-dom等依赖项,但react中的所用的路由方法是react-router-dom中。 默认情况下,create-react-app模板会自动生成一些文件和文件夹,这些文件和文件夹包括: node_modules:存储所有的项目依赖项。 p

    2024年02月09日
    浏览(42)
  • React18TS项目:配置react-css-modules,使用styleName

    他的好处不说了 网上一堆文章一个能打的都没有, 添加开发依赖 Babel Plugin \\\"React CSS Modules\\\" | Dr. Pogodin Studio 看@dr.pogodin/babel-plugin-react-css-modules官方文档 不使用babel-plugin-react-css-modules 手搭webpack配置需要处理 1.能启用css modules 对于裸 Webpack,请参见webpack css-loader的 modules 的选项

    2024年02月12日
    浏览(48)
  • React 18 使用 ref 操作 DOM

    参考文章 由于 React 会自动处理更新 DOM 以匹配渲染输出,因此在组件中通常不需要操作 DOM。但是,有时可能需要访问由 React 管理的 DOM 元素 —— 例如,让一个节点获得焦点、滚动它或测量它的尺寸和位置。在 React 中没有内置的方法来做这些事情,所以需要一个指向 DOM 节点

    2024年02月10日
    浏览(40)
  • 使用React 18和WebSocket构建实时通信功能

    WebSocket 是一种在 Web 应用中实现双向通信的协议。它允许服务器主动向客户端推送数据,而不需要客户端发起请求。在现代的实时应用中, WebSocket 经常用于实时数据传输、聊天功能、实时通知和多人协作等场景。在本篇博客中,我们将探索如何在 React 18 应用中使用 WebSocke

    2024年02月05日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包