Context API 可以说是 React 中最有趣的一个特性了。一方面很多流行的框架(例如react-reduxmobx-reactreact-router等)都在使用它;另一方面官方文档中却不推荐我们使用它。在 Context API 的文档中有下面这段话:

The vast majority of applications do not need to use context.

If you want your application to be stable, don’t use context. It is an experimental API and it is likely to break in future releases of React.

为何会出现这种情况呢?这还得让我们从现有版本 Context API 要解决的问题已经它自身的缺陷说起。

现有版本 Context API 的使用场景以及缺陷

我们都知道在 React 中父子组件可以通过 props 自顶向下的传递数据。但是当组件深度嵌套时,从顶层组件向最内层组件传递数据就不那么方便了。手动在每一层组件上逐级传递 prop 不仅书写起来很繁琐同时还会为夹在中间的组件引入不必要的 prop。这时 Context API 就派上用场了。你只需要在外层组件上声明要传递给子组件的 Context:

class Parent extends React.Component {
getChildContext() {
return {color: "purple"};
}
} Parent.childContextTypes = {
color: PropTypes.string
};

然后就可以在任意一级子组件上访问 Context 里的内容了:

class Child extends React.Component {
render() {
return (
<p>
{this.context.color}
</p>
);
}
} Child.contextTypes = {
color: PropTypes.string
};

现有 Context API 虽然使用起来不算复杂,但当和 shouldComponentUpdate 搭配使用时就很容易出问题。

让我们通过一个小例子来简单说明,假设有以下组件结构:

<A>
<B>
<C />
</B>
</A>

其中组件 A 会通过 getChildContext 设置 Context,组件 C 通过 this.context 读取 Context。

当组件 A 要更新 Context 的时候发生什么呢?

  1. 组件 A 通过 setState 设置新的 Context 值同时触发子组件的 rerender。
  2. 组件 B rerender。
  3. 组件 C rerender,并在自己的 render 方法中拿到更新后的Context。

整个流程看起来好像没什么问题。如果我们在组件 B 上定义了 shouldComponentUpdate 会发生什么呢?

  1. 组件 A 通过 setState 设置新的 Context 值同时触发子组件的 rerender。
  2. 组件 B 执行 shouldComponetUpdate,由于组件 B 自身并不依赖 Context,所以 shouldComponetUpdate 检测到 state 与 prop 均未变化因此返回 false。无需重新 render。
  3. 由于 B 组件没有 rerender。这导致组件 C 也不会rerender,因此也就无法获取到最新的 Context 值。

由于 shouldComponentUpdate 是一个 React 开发人员经常使用的优化方法。所以如果代码里使用了现有的 Context API 很大概率会遇到上述问题。

那么有没有解决方案可以让现有 Context API 和 shouldComponetUpdate 完美配合呢?答案是有的。这篇文章里有详细的讨论。但该方案属于比较 hack 的方式,且对新手并不友好。所以 React 的官方文档里并不建议我们使用现有的 Context API。

新版 Context API

这种情况在去年 12 月 7 号迎来了改变。@acdlite 在 reactjs/rfcs中发起了一个名为『New version of context』 的 PR。带来了全新的 Context API 提案。2 天后,包含新 Context API 具体实现的 PR 也提交到了 React 代码库。这两个 PR 都在今年 1 月 25 号被合并入各自的代码库。

首先让我们来看看新版 Context API 都由哪几部分组成:

  • React.createContext 方法用于创建一个 Context 对象。该对象包含 Provider 和 Consumer两个属性,分别为两个 React 组件。
  • Provider 组件。用在组件树中更外层的位置。它接受一个名为 value 的 prop,其值可以是任何 JavaScript 中的数据类型。
  • Consumer 组件。可以在 Provider 组件内部的任何一层使用。它接收一个名为 children 值为一个函数的 prop。这个函数的参数是 Provider 组件接收的那个 value prop 的值,返回值是一个 React 元素(一段 JSX 代码)。

用代码描述是这样的:

type Context<T> = {
Provider: Provider<T>,
Consumer: Consumer<T>,
}; interface React {
createContext<T>(defaultValue: T): Context<T>;
} type Provider<T> = React.Component<{
value: T,
children?: React.Node,
}>; type Consumer<T> = React.Component<{
children: (value: T) => React.Node,
}>;

以下是一段基于新 Context API 编写的代码:

import React from "react";
import { render } from "react-dom";
const NameContext = React.createContext("Jack");; class Hello extends React.PureComponent {
render() {
return (
<NameContext.Consumer>
{name => <h1>Hello *{name}* who come from the new Context API!</h1>}
</NameContext.Consumer>
);
}
} class App extends React.Component {
constructor() {
super(); this.state = {
name: "Michael"
}; this.modify = this.modify.bind(this);
} modify() {
this.setState({
name: this.state.name + "!"
});
} render() {
return (
<div>
<button onClick={this.modify}>Modify Context Value</button>
<NameContext.Provider value={this.state.name}>
{this.props.children}
</NameContext.Provider>
</div>
);
}
} render(
<App>
<Hello />
</App>,
document.getElementById("root")
);

以下是这段代码的运行效果。

下面让我们结合这段代码来说一说新版 Context API 的几个特点:

  1. Provider 和 Consumer 必须来自同一次 React.createContext 调用。也就是说 NameContext.Provider 和 AgeContext.Consumer 是无法搭配使用的。
  2. React.createContext 方法接收一个默认值作为参数。当 Consumer 外层没有对应的 Provider 时就会使用该默认值。
  3. Provider 组件的 value prop 值发生变更时,其内部组件树中对应的 Consumer 组件会接收到新值并重新执行 children 函数。此过程不受 shouldComponentUpdete 方法的影响。前面的示例代码中,Hello 组件继承自 React.PureComponent 但页面依然能正确显示足以说明这一点。
  4. Provider 组件利用 Object.is 检测 value prop 的值是否有更新。注意 Object.is 和 === 的行为不完全相同。具体细节请参考 Object.is 的 MDN 文档页
  5. Consumer 组件接收一个函数作为 children prop 并利用该函数的返回值生成组件树的模式被称为 Render Props 模式。详细介绍请参考相关 React 文档

以上就是关于 React 全新 Context API 的介绍了。按照计划,全新的 Context API 会随着 React 16.3.0 版本发布。如果你想现在就体验一把,可以通过以下命令安装 alpha 版本:

yarn add react@next react-dom@next
转载https://juejin.im/entry/5a9caa786fb9a028e46e2011

React 全新的 Context API的更多相关文章

  1. React 16.3来了:带着全新的Context API

    文章概览 React在版本16.3-alpha里引入了新的Context API,社区一片期待之声.我们先通过简单的例子,看下新的Context API长啥样,然后再简单探讨下新的API的意义. 文中 ...

  2. 简单整理React的Context API

    之前做项目时经常会遇到某个组件需要传递方法或者数据到其内部的某个子组件,中间跨越了甚至三四层组件,必须层层传递,一不小心哪层组件忘记传递下去了就不行.然而我们的项目其实并没有那么复杂,所以也没有使用r ...

  3. [译]迁移到新的 React Context Api

    随着 React 16.3.0 的发布,context api 也有了很大的更新.我已经从旧版的 api 更新到了新版.这里就分享一下我(作者)的心得体会. 回顾 下面是一个展示如何使用旧版 api ...

  4. React 新 Context API 在前端状态管理的实践

    本文转载至:今日头条技术博客 众所周知,React的单向数据流模式导致状态只能一级一级的由父组件传递到子组件,在大中型应用中较为繁琐不好管理,通常我们需要使用Redux来帮助我们进行管理,然而随着Re ...

  5. [React] Use the new React Context API

    The React documentation has been warning us for a long time now that context shouldn't be used and t ...

  6. 10分钟学会React Context API

    Create-react-app来学习这个功能: 注意下面代码红色的即可,非常简单. 在小项目里Context API完全可以替换掉react-redux. 修改app.js import React ...

  7. 手写一个React-Redux,玩转React的Context API

    上一篇文章我们手写了一个Redux,但是单纯的Redux只是一个状态机,是没有UI呈现的,所以一般我们使用的时候都会配合一个UI库,比如在React中使用Redux就会用到React-Redux这个库 ...

  8. React Hooks & Context API

    React Hooks & Context API responsive website https://reactjs.org/docs/hooks-reference.html https ...

  9. 解决React Native使用Fetch API请求网络报Network request failed

    问题来源: 1 . 在测试fetch数据请求时,Xcode9.0以上的无法请求https, 需要在Xcode中加载项目后修改Info.plist的相关配置,具体如下参考 问题及解决方法一模一样,不再重 ...

随机推荐

  1. HDU 6686 Rikka with Travels 树的直径

    题意:定义两点之间的距离为从一个点到另一个点经过的点数之和(包括这两个点),设二元组(x, y)为两条不相交的路径,一条长度为x,一条长度为y,问二元组(x, y)出现了多少次? 思路:直接上jls的 ...

  2. 关于嵌入式linux下的串口通讯问题---需增加回车/换行才能接收

    问题:在Linux应用层,直接从/dev/tty***使用read()函数读数据,无法读到,只有在数据末尾加上0a/0d才可以读到数据(这里是发送十六进制的数据,ASCLL码同理,增加回车才可以读到数 ...

  3. C# RSA加密

    /// <summary> /// 类名:RSACrypt /// 功能:RSA加密.解密.签名.验签 /// </summary> public sealed class R ...

  4. Java并发与多线程与锁优化

    前言 目前CPU的运算速度已经达到了百亿次每秒,所以为了提高生产率和高效地完成任务,基本上都采用多线程和并发的运作方式. 并发(Concurrency):是指在某个时间段内,多任务交替处理的能力.CP ...

  5. 存储系统设计——NVMe SSD性能影响因素一探究竟

    目录1 存储介质的变革 2 NVME SSD成为主流 2.1 NAND FLASH介质发展 2.2 软件层面看SSD——多队列技术 2.3 深入理解SSD硬件 3 影响NVME SSD的性能因素 3. ...

  6. Java Web学习总结(11)JDBC

    一,简介 JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的 ...

  7. iOS设计模式之工厂模式

    一,什么是工厂模式 模式定义: “专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类.” 世界上就是由一个工厂类,根据传入的参数,动态地决定创建出哪一个产品类的实例. 需求场景: 简 ...

  8. buuctf | [强网杯 2019]随便注

    1' and '0,1' and '1  : 单引号闭合 1' order by 3--+ : 猜字段 1' union select 1,database()# :开始注入,发现正则过滤 1' an ...

  9. ArrayList和Map的一些知识

    2014年10月17日16:39:01 1.如何获取某条数据在ArrayList中的索引值? 多个对象存储的ArrayList中,如果只知道对象其中的一个属性,如何获取该对象在ArrayList里的索 ...

  10. EditText设置/隐藏光标位置、选中文本和获取/清除焦点(转)

    转:http://blog.csdn.net/dajian790626/article/details/8464722 有时候需要让光标显示在EditText的指定位置或者选中某些文本.同样,为了方便 ...