React - Context 解决深层参数传递问题

前言

在一个参数需要传递非常多层 props 的时候,我们就需要考虑将其优化。

假设说 A 父组件获取到了一个参数 a,并且页面的嵌套状态为 A->B->C->D->E 只有 E 这个子组件需要参数 a,那么要是通过 props 的话,B C D 三个组件就也需要接收这个参数,并且完全没有使用,都只是向下传递。

这样就会造成很多不必要的歧义,假设在之后移除了组件 E,那么这么多的props需不需要删掉?删掉的话又是大工程,你需要确保这个参数在这些组件里面都已经无用了。总之这种做法会给开发带来很多的麻烦。

这个的解决方法可能挺多,可以通过透传,数据仓库,事件发布等等解决,具体需要看业务需求,那么让我们看看另外一种场景,在一个区域当中,有一个参数是很多组件通用的,比方说

js 复制代码
<Section>  
	<Heading level={3}>关于</Heading>  
	<Heading level={3}>照片</Heading>  
	<Heading level={3}>视频</Heading>  
</Section>

通用的level参数可能你需要重复写3次,并且要是需要的组件个数增多,就可能需要更多次,这些都属于重复代码。

今天就来介绍一下解决上面两个问题的一个方案 透传 context。

什么是 Context

context 是 传递 props 的另一种方法

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动传递 props

事实上官方设计这个 API 的目的是 共享可被视为React组件树的"全局"数据,例如当前经过身份验证的用户,主题或首选语言

意图言简意赅,我们都知道,React 组件是树状结构,我们可以在这个树状结构的某一个节点注入一个上下文对象,那么在这个节点下面的所有组件都能够使用这些数据。

Context 的用法

context 有两种使用方法,创建方法都是一样的,首先需要 React.createContext 这个方法来创建一个 context

并且这个对象上存在两个 API,分别是 ProviderConsumer

其中,Provider 是用来向组件树发出 context 对象,而 Consumer 是用来使用 Context 对象,要使用 Context 对象还有另外一种方法:使用 React.useContext 钩子,下面用两个例子来看一下两种使用方法。

首先我们可以在一个单独的文件创建我们的 context,因为 context 并不一定要和某个组件耦合,他可能可以用于任何地方,所以我们可以单独创建,之后在进行引入

js 复制代码
// LevelContext.js
import { createContext } from 'react';
  
export const LevelContext = createContext(1);

创建完了以后,我们就在需要使用的地方引入这个上下文对象,通过刚刚说的 Provider 我们可以将这个对象共享给组件树下面的组件。

js 复制代码
import { LevelContext, Provider, Consumer } from './levelContext.js';

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

上面是通过 LevelContext.Provider 来调用 Provider,当然你也可以即将他们分开来,这是完全一样的。

上面的 Section 组件,会输入一个子组件,然后将 LevelContext 这个上下文共享给子组件,value 就是我们需要共享的上下文

  1. 使用 React.useContext

然后我们先来看看用 React.useContext 接收上下文对象的写法,

js 复制代码
<Section level={1}>
	<AAA></AAA>
	<AAA></AAA>
	<AAA></AAA>
</Section>
js 复制代码
import { useContext } from 'react';
import { LevelContext } from './levelContext.js';

function AAA ({ }) {
  const level = useContext(LevelContext);
  return (
    <div>child {level}</div>
  )
}

这就是通过 React.useContext 的调用方法,页面能够正常展示我们传入的 1 .

  1. 通过 Consumer 引入,为了跟上面的那种方法做出对比,我们可以在上面的 AAA 组件种在加入另一种引入方法
js 复制代码
import { useContext } from 'react';
import { LevelContext, Consumer } from './levelContext.js';

function AAA ({ }) {
  const level = useContext(LevelContext);
  return (
    <div>child {level}
      <Consumer>
        {
          (context) => <p>{context}</p>
        }
      </Consumer>
    </div>
  )
}

页面还是能够正常展示上下文,两种方法需要在不同的情况下根据具体需求调用

Context 的优缺点

上面我们说明了 Context 能用来解决什么问题,但是这并不是一一定的,比方说多层传递 props 未必就比 context 要差,多层传递能够使得组件的可读性更好,但是 context 可能会在复杂组件中变得无法维护,因为你不知道他从哪里来的。并且 context 有就近的特点,多个上下文只有最近的那个会生效。

所以我们一般会在项目的跟组件定义一次上下文 centext ,以便于在整个项目都可以非常方便的使用,一些状态管理库也是基于这种工作逻辑。

总结

本文简单介绍了 context 以及一些使用方法,具体的使用还需要看具体需求,但是可以考虑在某些场景下使用 context,可能会获得更好的开发体验,1. 全局信息,包括但不限于:主题,用户信息,等等可能在项目全局任何地方用到的数据,2. 状态管理,状态管理的本质也就是将状态管理仓库通过 context 设定为全局状态,任何组件都可以去引用,获取库中的数据,或者借用库的方法来改变状态。

相关推荐
那就可爱多一点点2 小时前
H5页面多个视频如何只同时播放一个?
前端·音视频
前端郭德纲4 小时前
浅谈React的虚拟DOM
前端·javascript·react.js
2401_879103685 小时前
24.11.10 css
前端·css
ComPDFKit6 小时前
使用 PDF API 合并 PDF 文件
前端·javascript·macos
yqcoder6 小时前
react 中 memo 模块作用
前端·javascript·react.js
优雅永不过时·7 小时前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
神夜大侠9 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱9 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号10 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy729310 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html