React Context是一个强大的功能。如果你的React应用程序的规模增长超过了一个小的应用程序,给它一个尝试是没有错的。许多第三方库,如Redux,都在引擎盖下使用它,所以为什么不学习一下呢。
特别是当你的组件层次结构在垂直方向上增长时,将几个React组件传递下去会变得很乏味--从一个父组件到一个深度嵌套的子组件。大多数情况下,中间的所有React组件对这些道具不感兴趣,只是将道具传递给下一个子组件,直到它到达所需的子组件。
本教程给你一个使用React Context的简单用例的演练。
React Context为什么
你还记得上次你不得不在你的组件树下传递道具的时候吗?在React中,你经常会遇到这个问题,它被称为 "道具钻孔"。
+----------------+
| |
| A |
| |Props |
| v |
| |
+--------+-------+
|
+---------+-----------+
| |
| |
+--------+-------+ +--------+-------+
| | | |
| | | + |
| B | | |Props |
| | | v |
| | | |
+----------------+ +--------+-------+
|
+--------+-------+
| |
| + |
| |Props |
| v |
| |
+--------+-------+
|
+--------+-------+
| |
| + |
| |Props |
| C |
| |
+----------------+
反过来,这也会使每一个必须传递这些道具而不使用它们的组件变得混乱。React Context给了你一个摆脱这种混乱的方法。你可以通过React Context以隐式方式将道具传递给这些组件,而不是通过每个组件传递道具。如果一个组件需要访问上下文中的信息,它可以按需消费,因为一个顶级组件在上下文中提供了这些信息。
+----------------+
| |
| A |
| |
| Provide |
| Context |
+--------+-------+
|
+---------+-----------+
| |
| |
+--------+-------+ +--------+-------+
| | | |
| | | |
| B | | D |
| | | |
| | | |
+----------------+ +--------+-------+
|
+--------+-------+
| |
| |
| E |
| |
| |
+--------+-------+
|
+--------+-------+
| |
| C |
| |
| Consume |
| Context |
+----------------+
**React Context的用例是什么?**例如,设想你的React应用程序有一个颜色集的主题。在你的应用程序中,有各种组件需要了解该主题来设计自己的风格。因此,在你的顶层组件中,你可以让下面所有的React子组件都能访问这个主题。这就是React的Context发挥作用的地方。
+----------------+
| |
| A |
| |
| Provide |
| Theme |
+--------+-------+
|
+---------+-----------+
| |
| |
+--------+-------+ +--------+-------+
| | | |
| | | |
| B | | D |
| | | |
| | | |
+----------------+ +--------+-------+
|
+--------+-------+
| |
| |
| E |
| |
| |
+--------+-------+
|
+--------+-------+
| |
| C |
| |
| Consume |
| Theme |
+----------------+
**谁提供/使用React Context?**React组件A--我们的顶级组件--提供上下文,React组件C--作为子组件之一--消费上下文。不过,介于两者之间的是组件D和E。由于D和E组件并不关心这些信息,所以它们不消费上下文。只有组件C消耗它。如果组件A下面的任何其他组件想要访问上下文,它可以消费它。
React Context如何使用
首先,你必须创建React Context本身,它可以让你访问一个提供者和消费者组件。当你通过使用createContext ,用React创建上下文时,你可以给它传递一个初始值。初始值也可以是空的。
// src/ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(null);
export default ThemeContext;
其次,组件A将不得不为上下文提供给定的提供者组件。在这种情况下,它的value ,但它可以是任何东西,从组件的状态(如取来的数据)到道具。如果该值来自一个可修改的React状态,那么传递给提供者组件的值也可以改变。
// src/ComponentA.js
import React from 'react';
import ThemeContext from './ThemeContext';
const A = () => (
<ThemeContext.Provider value="green">
<D />
</ThemeContext.Provider>
);
组件A只显示组件D,虽然没有传递任何道具给它,但却让下面所有的React组件都能使用这个值green 。其中的一个子组件将是最终消耗上下文的组件C。
第三,在你的组件C中,在组件D下面,你可以消耗上下文对象。注意,组件A不需要通过组件D在props中传递任何东西,以便它到达组件C。
// src/ComponentC.js
import React from 'react';
import ThemeContext from './ThemeContext';
const C = () => (
<ThemeContext.Consumer>
{value => (
<p style={{ color: value }}>
Hello World
</p>
)}
</ThemeContext.Consumer>
);
组件可以通过消耗上下文来获得其风格。消费者组件通过使用一个渲染道具使传递的上下文可用。你可以想象,按照这种方式,每个需要根据主题进行样式设计的组件都可以通过使用ThemeContext的Consumer组件从React的Context获得必要的信息。你只需要使用提供者组件,在他们上面的某个地方传递一次值。
React Context什么时候
你什么时候应该使用React Context?一般来说,有两种情况下要使用它:
- 当你的React组件层次结构垂直增长时,你希望能够将道具传递给子组件而不影响中间的组件。在整个React Context教程中,我们都以这个用例为例。
- 当你想在React中使用React Hooks进行高级状态管理,通过React Context在你的React应用中传递状态和状态更新函数。通过React Context做到这一点,你就可以创建一个共享的全局状态。
一个使用React Context的运行中的应用程序可以在这个GitHub仓库中找到。毕竟,React Context是一种向深度嵌套的React组件传递道具的好方法,因为它不会打扰中间的组件。