本文为翻译文章,原文再次React Context in 5 Minutes
React Context API已经成为了很多状态管理工具的选择,通常情况下可以完全取代Redux,在这个5分钟了解React Context教程当中,你将会了解到Context是什么并且如何使用它!
首先,看一下下面这棵树,树的底部是一个个的组件
你可以很轻松的在底部组件里面增加state,但到目前为止,将数据传递给同级组件的唯一方法就是将state传递到更高的组件当中,然后又通过props将其传递给同级组件。
但是如果稍后我们发现使用state的组件的同级也需要数据,则必须再次传递state,并通过props将其传回。
虽然这样可以解决问题,但是如果在不同分支上的组件都需要问题,则会出现很多问题。
在这个案例当中,我们需要将state从顶层的应用通过各个中间组件传递到最底层的组件,即使中间的那些组件不需要这个state。这一繁琐而耗时的过程被称为prop drilling。
这就是 Context 这个 API 需要存在的场景。它提供了一种不再需要层层传递props就可以将数据传递到最底层的方法。你可以把它想象成一个数据捕捉的组件-甚至中间的那些组件根本不知道发生了什么。
为了演示这一点,我们创建了这个美观(而且非常有用)的昼夜切换图像。
如果你想看完整的代码,请务必查看这篇文章
创建上下文
首先,我们创建一个新的Context,由于我们希望整个应用程序能够访问该应用,所以我们将index.js并将应用程序包装在ThemeContext.Provider中。
import React from 'react';
import ReactDOM from 'react-dom';
import ThemeContext from './themeContext';
import App from './App';
ReactDOM.render(
<ThemeContext.Provider value={"Day"}>
<App />
</ThemeContext.Provider>,
document.getElementById('root')
)
在App.js,我们返回一个简单的组件
import React from 'react';
import Image from './Image';
class App extends React.Component {
render() {
return (
<div className="app">
<Image />
</div>
)
}
}
export default App;
我们的目标是在Image.js中使用Context将className从Day切换到Night,这取决于我们想要渲染哪个图片。为了做到这一点,我们将一个静态属性添加到我们的组件ContextType,并且使用字符串插入到Image组件里面的className当中
import React from 'react';
import Button from './Button';
import ThemeContext from './themeContext';
class Image extends React.Component {
render() {
const theme = this.context;
return (
<div className={`${theme}-image image`}>
<div className={`${theme}-ball ball`} />
<Button />
</div>
)
}
}
Context.Consumer
不幸的是,此方法仅适用于基于类的组件。如果你已经学习了Hooks in React,你会知道我们现在可以用function组件做任何事情。所以为了更好的衡量,我们应该将组件转换为function组件,同时使用ThemeContext.Consumer通过app传递信息。
这是通过将我们的元素封装在<ThemeContext.Consumer>的实例中来完成的,并且在该实例中(子元素所在的位置)提供了一个返回元素的函数。这使用了 render prop 模式,在这种模式中,我们作为子函数提供一个常规函数,返回一些JSX来呈现。
import React from 'react';
import Button from './Button';
import ThemeContext from './themeContext';
function Image(props) {
// 我们不再需要
// const theme = this.context;
return (
<ThemeContext.Consumer>
{theme => (
<div className={`${theme}-image image`}>
<div className={`${theme}-ball ball`} />
<Button />
</div>
)}
</ThemeContext.Consumer>
)
}
export default Image;
注意: 我们也需要将Button组件包裹在<ThemeContext.Consumer>,这允许我们稍后向按钮添加功能。
import React from "react";
import ThemeContext from "./themeContext";
function Button(props) {
return (
<ThemeContext.Consumer>
{context => (
<button className="button">
Switch
<span role="img" aria-label="sun">
🌞
</span>
<span role="img" aria-label="moon">
🌚
</span>
</button>
)}
</ThemeContext.Consumer>
);
}
export default Button;
Extract Context Provider
我们目前正在通过Provider传递一个硬编码的值