我们知道在react当中,组件与组件的沟通是通过props,父组件通过props给子组件传递一些属性,父组件可以传递一些回调函数给子组件,让子组件在某些特定的时候,可以调用父组件的一些特性。
但是我们会存在一个问题就是,react 的应用当中,组件和组件之间并不一定只有父子关系。他们还会存在着父子嵌套多层之后,第一层和最后一层之间是一个祖孙的关系。他们之间会隔着好几层不同的组件。那如果他们之间要进行一些沟通,我们通过 props 传递是不太现实的。因为中间的那几层组件不一定是我们自己写的。而且中间这个组件要去传递这个 props ,对她来说是完全没有意义的事情,所以这个时候 react 就给我们提供了 context 的使用方式
上级组件当中我们提供了一个 context 对象之后,他的子元素里面都可以通过 context 去获取到他提供的这部分内容,以此达到一个跨越多层组件传递信息的一个功能。
context有两种实现方式
1、childContextType (即将在 react 17 版本的时候废弃)
2、createContext (react 16 之后提供的)
代码
import React from 'react'
import PropTypes from 'prop-types' const { Provider, Consumer } = React.createContext('default') class Parent extends React.Component {
state = {
childContext: '123',
newContext: '456',
} getChildContext() {
return { value: this.state.childContext, a: 'aaaaa' }
} render() {
return (
<>
<div>
<label>childContext:</label>
<input
type="text"
value={this.state.childContext}
onChange={e => this.setState({ childContext: e.target.value })}
/>
</div>
<div>
<label>newContext:</label>
<input
type="text"
value={this.state.newContext}
onChange={e => this.setState({ newContext: e.target.value })}
/>
</div>
<Provider value={this.state.newContext}>{this.props.children}</Provider>
</>
)
}
} class Parent2 extends React.Component {
// { value: this.state.childContext, a: 'bbbbb' }
getChildContext() {
return { a: 'bbbbb' }
} render() {
return this.props.children
}
} function Child1(props, context) {
console.log(context)
return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
} Child1.contextTypes = {
value: PropTypes.string,
} class Child2 extends React.Component {
render() {
return (
<p>
childContext: {this.context.value} {this.context.a}
</p>
)
}
} // Child2.contextType = Consumer Child2.contextTypes = {
value: PropTypes.string,
a: PropTypes.string,
} Parent.childContextTypes = {
value: PropTypes.string,
a: PropTypes.string,
} Parent2.childContextTypes = {
a: PropTypes.string,
} export default () => (
<Parent>
<Parent2>
<Child1 />
<Child2 />
</Parent2>
</Parent>
)
第一种,childContextType
export default () => (
<Parent>
<Parent2>
<Child2 />
</Parent2>
</Parent>
)

首先组件层级是这样的,这里 child2 子级要使用 parent 的数据。要使用 childContextType,必须先声明

Parent.childContextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
然后在 Parent 组件里面的 getChildContext 方法可以设置数据
getChildContext() {
return { value: this.state.childContext, a: 'aaaaa' }
}
设置好之后,在 Child2 里面使用 childContextType,也需要先声明
Child2.contextTypes = {
value: PropTypes.string,
a: PropTypes.string,
}
声明好之后,则可以在 Child2 里面使用
render() {
return (
<p>
childContext: {this.context.value} {this.context.a}
</p>
)
}
为什么一定要这么做呢?因为在 react 中,Child2 的上层组件不一定只有一个,他上层组件提供的 context 也不一定只有一个。他们是有一个 merge 的关系的。所以 react 需要知道你获取的是 context 的哪几个属性。这是老的 context 的用法
第二种,createContext
新的context,是通过 React.createContext,他返回一个对象,这个对象包含 Provider , Consumer
const { Provider, Consumer } = React.createContext('default')
分别是 context 的提供方 和 context 的订阅方。那么这两个都是组件
<Provider value={this.state.newContext}>{this.props.children}</Provider>
我们通过 Provider 这个组件,然后上面指定 value。这个 value 就是这个 context 他对应的属性。在他的子组件下面,只需要去通过 Consumer 这个组件,然后传入的是一个方法,这个方法我们认为是 function component 。他接收这个 value,并且把想要渲染的东西渲染出来就可以了
 <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
所以 Provider 和 Consumer 是一一对应的关系。在上层组件用到之后,在子组件里面什么时候用到,就什么时候用 Consumer 去获取
那么 react 为什么要弃用老的 api, 使用这种新的 api 呢,因为老的 api 他对于 context 的提供方他下层的影响太大了。他会导致他下层的所有组件,即便是没有任何更新的情况下 他每次更新的过程中仍然要进行完整的渲染。所以对性能的损耗非常大。
这就是我们 context 的两种方式,接下来我们来看下 createContext 的源码,打开 RreateContext
import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';

import type {ReactContext} from 'shared/ReactTypes';

import warningWithoutStack from 'shared/warningWithoutStack';
import warning from 'shared/warning'; export function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
} else {
if (__DEV__) {
warningWithoutStack(
calculateChangedBits === null ||
typeof calculateChangedBits === 'function',
'createContext: Expected the optional second argument to be a ' +
'function. Instead received: %s',
calculateChangedBits,
);
}
} const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
// These are circular
Provider: (null: any),
Consumer: (null: any),
}; context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
}; context.Consumer = context; return context;
}

我们看到 createContext ,他接收的是一个 defaultValue ,还有一个是 calculateChangedBits 。是一个方法,是用来计算新老 context 的一个变化的。方法里面看到他声明了一个 context 对象,这个对象跟之前看的 ReactElement 非常的像。也有一个  $$typeof ,这个  $$typeof 跟 ReactElement 的  $$typeof 是不一样的。还有两个属性 _currentValue , _currentValue2 这两个属性是一样的,只是用到的地方不一样。 _currentValue 这个 value 是用来记录 Provider 里面的这个 value 。 他有变化的情况下就会更新到这个 _currentValue 。这就是用来记录最新的 context 的值的, 下面会有个 Provider 和 Consumer 。

下面有个 context.Provider ,他有个属性 _context ,这个属性会指向这个 context。context.Consumer = context。 也就是说 Consumer 是等于自己的。
到这我们知道 Consumer 就是指向这个对象的时候,可以猜到,在 Consumer 进行渲染的时候,他要去获取这个 value 怎么办呢,他只要从自己本身上面 _currentValue 就可以拿到最新的 context 的 value 。然后再调用那个方法把他传进去。这就是基本的实现原理,真正的实现没有这么简单。
这里面的 $$typeof 并不是要替换 ReactElement 的 $$typeof。而是这里面的 $$typeof 是指 ReactElement 里面的 type.

React源码 ReactContext的更多相关文章

  1. React源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理.本系列文章希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(C ...

  2. React源码解析:ReactElement

    ReactElement算是React源码中比较简单的部分了,直接看源码: var ReactElement = function(type, key, ref, self, source, owne ...

  3. react 源码之setState

    今天看了react源码,仅以记录. 1:monorepo (react 的代码管理方式) 与multirepo 相对. monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个mo ...

  4. React 源码剖析系列 - 不可思议的 react diff

      简单点的重复利用已有的dom和其他REACT性能快的原理. key的作用和虚拟节点 目前,前端领域中 React 势头正盛,使用者众多却少有能够深入剖析内部实现机制和原理. 本系列文章希望通过剖析 ...

  5. React 源码剖析系列 - 生命周期的管理艺术

    目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期 ...

  6. 读react源码准备

    git源码地址:https://github.com/facebook/react react 里面就是 react源码 react里面的react文件夹就是react源码,react源码非常的少,总 ...

  7. react源码之render

    1.最近学习react源码,刚刚入门,看了render的原理,到了fiberRoot的创建 如图:

  8. React躬行记(16)——React源码分析

    React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版 ...

  9. React源码之组件的实现与首次渲染

    react: v15.0.0 本文讲 组件如何编译 以及 ReactDOM.render 的渲染过程. babel 的编译 babel 将 React JSX 编译成 JavaScript. 在 ba ...

随机推荐

  1. 【技术博客】使用PhpStorm和Xdebug实现Laravel工程的远程开发及调试

    目录 使用PhpStorm和Xdebug实现Laravel工程的远程开发及调试 简介 PhpStorm中的远程开发 1. 配置服务器 2. 配置路径对应 3. 配置同步 4. 进行代码同步 5. 优点 ...

  2. ASP.NET Core如何在ActionFilterAttribute里做依赖注入

    在ASP.NET Core里,我们可以使用构造函数注入很方便地对Controller,ViewComponent等部件做依赖注入.但是如何给过滤器ActionFilterAttribute也用上构造函 ...

  3. Debug 路漫漫-09:构建CNN时维度不一致问题

    Build CNN Network 之后,运行,但是报错: ValueError: Input 0 is incompatible with layer predict_vector_conv1: e ...

  4. Maven设置MAVEN_OPTS环境变量

    原文地址:https://blog.csdn.net/porsche_gt3rs/article/details/78787491 一 原因: 运行mvn命令实际是执行java命令,既然是运行java ...

  5. python装饰器的使用场景(转)

    原文:https://www.cnblogs.com/wupeiqi/articles/4980620.html 1.必备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #### ...

  6. Git 管理版本/回退

    参考链接:https://www.liaoxuefeng.com/wiki/896043488029600/896954074659008 Git status命令可以让我们时刻掌握仓库当前的状态,比 ...

  7. tqdm()与set_description()的用法

    pbar=tqdm(range(55156))for i in pbar: # print(i) a=464443161*845113131 pbar.set_description("tr ...

  8. Linux下搭建keepalive+nginx

    一. 安装nginx(略) 二. 安装keepalive 下载http://www.keepalived.org/download.html 安装依赖包 yum install –y popt* gc ...

  9. drf框架--基础

    目录 drf框架 导入 什么是接口 restful接口规范 原生Django实现接口 drf框架 Django CBV 和drf CBV对比 响应渲染模块 请求数据解析模块 响应模块 二次封装Resp ...

  10. JavaWeb第一天--HTML

    HTML HTML简介 HTML(Hyper TextMarkupLanguage) 超文本标记语言. 超文本: 超越了文本仅仅展示信息的功能范畴,泛指:图片.有样式的文字.超链接. 标记: 标签. ...