在学习redux源码一段时间之后,我发现不懂得context
如何使用已经阻碍到我理解redux的设计思想了。。
所以,这里介绍一下react的context
如何使用,不过这个api可能你在业务开发时永远不会用到。。
好了,废话不多说,我们来讲解下context
如何使用。
一. 如何使用context?
想象一下,我们有现在这样一个页面(Index
),其组件树长成如下这个样子:
假设这个组件树里的任意一个组件都可以去更新Index
中的属性themeColor
,这个时候就有了状态提升
的问题,需要把状态提升到Index
中,然后一级一级传下来。
这里的问题是非常明显的,需要把状态一级一级传递下去,如果组件层次很深的话,可维护性会非常非常差,简直就是灾难。
如果这棵组件树长这个样子就好了,所有组件可以去一个公共区域
去获取状态,这样不管组件层次有多深,每个组件都可以去这个公共区域
去获取状态。
我们实现一下这个组件树
class Index extends Component {
render () {
return (
<div>
<Header />
<Main />
</div>
)
}
}
class Header extends Component {
render () {
return (
<div>
<h2>This is header</h2>
<Title />
</div>
)
}
}
class Main extends Component {
render () {
return (
<div>
<h2>This is main</h2>
<Content />
</div>
)
}
}
class Title extends Component {
render () {
return (
<h1>React.js 小书标题</h1>
)
}
}
class Content extends Component {
render () {
return (
<div>
<h2>React.js 小书内容</h2>
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
接下来我们基于以上代码,来介绍如何使用context。
class Index extends Component {
static childContextTypes = {
themeColor: PropTypes.string
}
constructor () {
super()
this.state = { themeColor: 'red' }
}
getChildContext () {
return { themeColor: this.state.themeColor }
}
render () {
return (
<div>
<Header />
<Main />
</div>
)
}
}
先声明一个静态变量childContextTypes
,这个变量名是固定的。然后,我们在state里初始化一个状态,然后在getChildContext
(这个方法名也是固定的)里设置context
。
以上操作,我们就完成了父组件的context
设置工作,接下来,我们看下在子组件里如何去取context
。
class Title extends Component {
static contextTypes = {
themeColor: PropTypes.string
}
render () {
return (
<h1 style={{ color: this.context.themeColor }}>React.js 小书标题</h1>
)
}
}
子组件要想获得context,必须要声明contextTypes
,这个变量名也是固定的,必须要这样写。
接下来,我们就可以在子组件中通过this.context
来获取context
中的值,可以在父组件中通过this.setState
来修改context中的值。
二. 更直观地使用context
在最近几个版本中,React对context
的api
进行了调整,更加明确了生产者消费者
的使用方式。
import React from 'react';
const ThemeContext = React.createContext({
themeColor: 'red',
});
通过静态方法React.createContext
创建一个context
对象,这个context
对象包含两个组件:Provider
和Consumer
。
class App extends React.Component {
render () {
return (
<ThemeContext.Provider value={{themeColor: 'green'}}>
<Header />
</ThemeContext.Provider>
);
}
}
Provider
的value
相当于getChildContext()
。
class Header extends React.Component {
render () {
return (
<Title>Hello React Context API</Title>
);
}
}
class Title extends React.Component {
render () {
return (
<ThemeContext.Consumer>
{context => (
<h1 style={{background: context.themeColor}}>
{this.props.children}
</h1>
)}
</ThemeContext.Consumer>
);
}
}
Consumer
的children
必须是一个函数,通过函数的参数获取Provider
提供的Context
。
三. 总结
新版本的react
里对context
的api
调整过后,开发者能够更加直观地理解context
的使用方式。
四. 参考
https://juejin.im/post/5a90e0545188257a63112977
https://www.jianshu.com/p/c7c47ead84c7
https://react.docschina.org/docs/context.html
https://yepbug.com/2018/11/11/react-context-and-simple-redux/
http://huziketang.mangojuice.top/books/react/lesson29