我们知道在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. 如何配置maven的环境变量

    安装maven后,这是maven的安装路径 打开:我的电脑——右键——属性——高级系统设置——环境变量 第一步:点击“系统变量(S)”下面的“新建(W)...”按钮,在“新建系统变量”中输入变量名MA ...

  2. SpringBoot扩展点之一:SpringApplicationRunListener

    三种监听器的关系 ApplicationListener.SpringApplicationRunListeners.SpringApplicationRunListener的关系: SpringAp ...

  3. Sitecore客户体验成熟度模型之旅

    “成熟”这个词带来了很多想法:你在青年时不愿意找到工作?你四岁的孩子偶尔发脾气?可能还有你的公司能否在数字化时代提供个性化的客户体验? 你如何定义CX成熟度?如果您的CX战略仍处于开发阶段,您需要达到 ...

  4. 单词uranolite陨石uranolite英语

    陨石(uranolite)是指来自地球以外太阳系其他天体的碎片,绝大多数来自位于火星和木星之间的小行星,少数来自月球(40块)和火星(40块).全世界已收集到4万多块陨石样品,石陨石主要成分是硅酸盐. ...

  5. CSP2019退役记

    写在前面 all last,我又失败了,我退役了 回忆我这个菜鸡OI生涯,有看机房神仙切题的乐趣,也有自己考场爆炸的辛酸 NOIP2017,我第一次参赛,我pj205二等打铁 NOIP2018,我第二 ...

  6. MyBatis系列(三) MyBatis 配置文件

    一.properties 此标签的主要作用是引用配置文件,以数据源来举例. 新建mybatis-confing.properties配置文件 mybatis-confing.properties dr ...

  7. 可落地的DDD的(2)-为什么说MVC工程架构已经过时

    摘要 mvc是一种软件设计模式,最早由Trygve Reenskaug在1978年提出,他有效的解决了表示层,控制器层,逻辑层的代码混合在一起的问题,很好的做到了职责分离.但是在实际的编码实践过程中, ...

  8. K8S学习笔记之Grafana App for Kubernetes的配置

    Grafana有一套针对Kubernetes监控的APP,和Grafana-Zabbix App类似,但是配置咋一看比较麻烦,主要参数都是来自K8S. 这款APP的详细介绍请参考Grafana App ...

  9. ASP.NET Core中app.UseDeveloperExceptionPage和app.UseExceptionHandler方法有什么用

    在新建一个ASP.NET Core项目后,在项目Startup类的Configure方法中默认会添加两个方法的调用,app.UseDeveloperExceptionPage和app.UseExcep ...

  10. oracle grant 授权语句

    --select * from dba_users; 查询数据库中的所有用户 --alter user TEST_SELECT account lock; 锁住用户 --alter user TEST ...