我的掘金地址:https://juejin.im/post/5c9b10...
1. Context
关于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.
- If you aren't familiar with state management libraries like Redux or MobX, don't use context.
- If you aren't an experienced React developer, don't use context. There is usually a better way to implement functionality just using props and state.
综上所述就是不要使用context这个API。
虽然说不要用,但是我们也是要了解下这个API到底是干嘛的,毕竟有些优秀的库都是通过这个API实现而来,如:React-Redux。
简单了解context的作用就是在某个父组件中定义一个全局状态,这个状态可以在该父组件下的所有子组件中跨级传递共享。目前有两个版本分别是16.x之前和16.x之后的版本。
2. 老版本的Context
在老版本中有如下几个方法:
getChildContext: 在父组件中声明一个函数,返回的结果是一个对象,这个对象就是context,可以对子组件进行共享的状态。
childContextTypes: 在父组件中声明,执行context中的数据类型,如果不指定会产生错误。
contextTypes: 在子孙组件中进行声明,指定要接受context中哪些数据类型。
Tip: React.PropTypes has moved into a different package since React v15.5. Please use the prop-types library instead to define contextTypes.如上,react15.5已经弃用React.PropTypes,需要安装prop-types库。
看个小例子:
//父组件
import React from 'react'
import DemoSun from './componets/DemoSun'
import propTyps from 'prop-types'
class Demo extends React.Component {
getChildContext() {
return {
color: 'red'
}
}
render() {
return (
<div>
DEMO
我是什么颜色的太阳:<DemoSun />
</div>
)
}
}
Demo.childContextTypes = {
color: propTyps.string
}
export default Demo
//子组件
import React from 'react'
import propTyps from 'prop-types'
class DemoSun extends React.Component {
render() {
return (
<div>
DemoSun
{this.context.color}
</div>
)
}
}
DemoSun.contextTypes = {
color: propTyps.string
}
export default DemoSun
结果如下,子组件可以获取context中的color的值。
如果contextTypes定义在某个组件中,则这个组件的生命周期函数中会增加一个参数:
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
如果在无状态组件中使用context则如下:
const PropTypes = require('prop-types');
const Button = ({children}, context) =>
<button style={{background: context.color}}>
{children}
</button>;
Button.contextTypes = {color: PropTypes.string};
关于老版的context就介绍到此,来关注下新版本的context。
3. 新版本的Context
新版本中使用Provider和Consumer模式,在顶层Provider中传入value,在子孙中的Consumer中获取该值,并且能够传递函数,用来修改context。
- React.createContext(args):
const Mycontext = React.createContext(defaultValue)
新版的是通过该方法初始化一个context对象。当React渲染了一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配Provider中读取到当前的context值。只有当组件所处的树中没有匹配到Provider时,其defaultValue参数才会生效。
- Context.Provider
<Mycontext.Provider value={/*某个值*/}></Mycontext.Provider>
每个Context对象都会返回一个Provider组件。它允许消费组件订阅context变化。其有一个value属性,传递给消费组件。一个Provider可以和多个消费组件有对应关系,多个Provider也可以嵌套使用,里层的会覆盖外层数据。
当Provider的value值发生变化时,它内部的所有消费者组件都会重新渲染。Provider及其内部consumer组件都不受shouldComponentUpdate函数的影响,无论shouldComponentUpdate返回true或者false,因此当consumer组件在其祖先组件退出更新的情况下也可以更新。
- Class.contexType
挂载在class上的contextType静态属性会被赋值为一个由React.createContext()的Context对象。这能让你使用this.context来消费最近Context上的那个值。你可以在任何生命周期中访问它,包括在render中。
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
MyClass.contextType = MyContext;
- Context.Consumer
<MyContext.Consumer>
{value=>/*基于context值进行渲染*/}
</MyContext.Consumer>
这里,React组件也可以订阅到context变更。这能让你在函数式组件中完成订阅context。Consumer的children必须是一个函数。
这需要函数作为子元素这种做法。这个函数接受当前的context值,返回一个react节点。传递给函数的value值等同于往上组件树离这个context最近的Provider提供的value值。如果没有对应的Provider,value参数等于传递给createContext()的defaultValue。
4. 注意事项
context会使用参考标识来决定何时进行渲染。这样就会当provider的父组件进行重新渲染时,可能会在consumer组件中触发意外的渲染。如下:
class App extends React.Componenet{
render() {
return (
<Provider value={{text: 'text'}}>
<Demo />
</Provider>
)
}
}
如上,每次value都会创建一个新的对象。为了避免这种情况,我们可以将其提出到state中进行管理。
class App extends React.Componenet{
constructor(props) {
super(props)
this.state = {
text: 'text'
}
}
render() {
return (
<Provider value={{text: this.state.text}}>
<Demo />
</Provider>
)
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。