前言
在一个参数需要传递非常多层 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
,分别是 Provider
和 Consumer
其中,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 就是我们需要共享的上下文
- 使用
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 .
- 通过
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
设定为全局状态,任何组件都可以去引用,获取库中的数据,或者借用库的方法来改变状态。