我们知道在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. Linux简介和各发行版介绍

    一.Linux 简介 Linux 内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在大学上学时出于个人爱好而编写的. Linux 是一套免费使用和自由传播的类 Unix 操作系统,是 ...

  2. SQL工具 Red Gate

    Red Gate提供了很多对于sql server的工具. 这边介绍两个:Sql Prompt和Sql doc Sql Prompt:智能提示sql语句等等 Sql doc:生成数据库文档页面 Red ...

  3. 在Apache中安装php5.6 & php7.3

    1.下载 httpd-2.4.41-win64-VC15.zip.php5.6 +  vc11.  php7.3  + vc14-16 2.配置httpd,在 httpd.conf L180 添加如下 ...

  4. Django 安装使用

    Django 安装使用 Django 介绍: Django是一个开放源代码的Web应用框架,由Python写成.采用了MVT的框架模式,即模型M,视图V和模版T.它最初是被开发来用于管理劳伦斯出版集团 ...

  5. Angular的Observable可观察对象(转)

    原文:https://blog.csdn.net/qq_34414916/article/details/85194098 Observable 在开始讲服务之前,我们先来看一下一个新东西——Obse ...

  6. 【题解】古代猪文 [SDOI2010] [BZOJ1951] [P2480]

    [题解]古代猪文 [SDOI2010] [BZOJ1951] [P2480] 在那山的那边海的那边有一群小肥猪.他们活泼又聪明,他们调皮又灵敏.他们自由自在生活在那绿色的大草坪,他们善良勇敢相互都关心 ...

  7. golang学习笔记 ---interface

    1. 什么是interface接口 interface 是GO语言的基础特性之一.可以理解为一种类型的规范或者约定.它跟java,C# 不太一样,不需要显示说明实现了某个接口,它没有继承或子类或“im ...

  8. python数据分析三剑客之: Numpy

    数据分析三剑客之: Numpy 一丶Numpy的使用 ​ numpy 是Python语言的一个扩展程序库,支持大维度的数组和矩阵运算.也支持针对数组运算提供大量的数学函数库 创建ndarray # 1 ...

  9. Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终端打印SQL语句,脚本调试)

    Django---Django的ORM的一对多操作(外键操作),ORM的多对多操作(关系管理对象),ORM的分组聚合,ORM的F字段查询和Q字段条件查询,Django的事务操作,额外(Django的终 ...

  10. Beego 学习笔记15:布局页面

    页面布局 1>     一个html页面由:head部分,body部分,内部css,内部js,外联css,外联的js这几部分组成.因此,一个布局文件也就需要针对这些进行拆分. 2>     ...