>ContextAPI其实是一个存在了很久的特性,但是旧的API存在很多问题因此使用度不高。React16.3之后官方更新了ContextApi。
之前我们使用传递props的方法在组件之间传值,但是在组件层级多了之后会导致要在每一层都加上要传递的props,维护起来很麻烦,一旦新增或者减少,所有传递了该props的组件都要调整,增加了开发的工作量。为了解决这个问题,React引入了ContextApi
<br />
### Context包含的内容
- React.CreateContext()方法
- 该方法接受一个默认值作为参数,默认值可以为Number,String,Object,Boolean,甚至可以接受一个函数作为参数。
- Context.Provider 上下文支持者组件
- 每一个Context都包含一个Provider组件,该组件接受一个名为value的props,该值将会取代CreateContext中的默认值。一旦使用该组件,就算没有传递value或者value为undefined、NULL,默认值也将不起作用。
- Context.Consumer 上下文消费者组件
- 每一个Context在提供Provider组件的同时也会提供一个Consumer组件,作为该Context的使用者。
- Class.contextType
- 所有的类组件都会有一个contextType属性,通过这个属性我们可以把我们创建的context赋值给类组件,并在组件中通过使用this.context来获取当前context的值。
**Consumer可以脱离Provider的情况下单独使用,在没有Provider组件时,将会使用默认值,如果存在多个Provider组件,将会使用最近的Provider组件提供的value作为自己的值**
<br />
### context最基本的应用
```javascript
import React, { Component } from 'react';
const firstContext = React.createContext('一个使用了context的应用');
const ContextComp = () => {
return (
<firstContext.Consumer>
{
contextValue => <h1>{contextValue}</h1>
}
</firstContext.Consumer>
);
};
const ContextFather = () => {
return <ContextComp />;
};
class ContextTest extends Component {
render() {
return <ContextFather />
}
}
export default ContextTest;
```
![](https://box.kancloud.cn/6811c15595e271a5459a63a0aa7f701c_567x114.png)
<br />
### 使用Context.Provider改变context的值
```javascript
import React, { Component } from 'react';
const firstContext = React.createContext('一个使用了context的应用');
const ContextComp = () => {
return (
<firstContext.Consumer>
{
contextValue => <h1>{contextValue}</h1>
}
</firstContext.Consumer>
);
};
const ContextFather = () => {
return <ContextComp />;
};
class ContextTest extends Component {
render() {
return (
<firstContext.Provider value={123}>
<ComtextFather />
</firstContext.Provider>
)
}
}
export default ContextTest;
```
![](https://box.kancloud.cn/e7bf4e571e201012601b3ead39affb6c_229x100.png)
<br />
### 动态设置context值的实现方法
```javascript
import React, { Component } from 'react';
const firstContext = React.createContext('一个使用了context的应用');
const ContextComp = () => {
return (
<firstContext.Consumer>
{
contextValue => <h1>{contextValue}</h1>
}
</firstContext.Consumer>
);
};
const ContextFather = () => {
return <ContextComp /> ;
};
class ContextTest extends Component {
state = {
title: 'context测试标题',
}
render() {
return (
<firstContext.Provider value={this.state.title}>
<ContextFather />
</firstContext.Provider>
);
}
}
export default ContextTest;
```
![](https://box.kancloud.cn/0021281d0d897dd9f44eb90aad9552c9_696x165.png)
<br />
### 使用对象作为context的值
```javascript
import React, { Component } from 'react';
const firstContext = React.createContext({ title: '这是一个使用了context的组件' });
const ContextComp = () => {
return (
<firstContext.Consumer>
{
contextValue => <h1>{contextValue.title}</h1>
}
</firstContext.Consumer>
);
};
const ContextFather = () => {
return <ContextComp /> ;
};
class ContextTest extends Component {
state = {
title: 'context测试标题',
}
render() {
const { title } = this.state;
return (
<firstContext.Provider value={{ title }}>
<ContextFather />
</firstContext.Provider>
);
}
}
export default ContextTest;
```
![](https://box.kancloud.cn/71a6b92200ad447e866828741bed7f2f_641x150.png)
<br />
### 引用外部的Context
在使用中我们可以将Context写入单独的文件,再通过export、import的方式引入Context
在contestTest.jsx中暴露了自定义Context的Provider,Consumer
```javascript
import React from 'react';
export const { Consumer, Provider } = React.createContext({ title: '引用外部的context' });
```
然后在context.jsx中引入{ Consumer, Provider }
```javascript
import React, { Component } from 'react';
import { Provider, Consumer } from './contextTest';
const ContextComp = () => {
return (
<Consumer>
{
contextValue => <h1>{contextValue}</h1>
}
</Consumer>
);
};
const ContextFather = () => {
return <ContextComp />
};
class ContextTest extends Component {
render() {
return (
<Provider value={{ title: '修改引用的外部title' }}
<ContextFather />
</Provider>
);
}
}
export default ContextTest;
```
![](https://box.kancloud.cn/b44394bf733836c57e0208956cbcfa96_492x137.png)
<br />
### 需要注意的点
有人说Context是React中的Redux,在我看来并不是这样。Context有他自己擅长的地方,也有着他的局限性。
1. Context适用于多层级组件之间只用相同的props的情况,一旦组件之间使用的数据结构有一点点变化,我们就需要写一个新的Context来满足组件的使用,如果使用的context是通过引用的方式使用的,那么就需要新写一个context文件,而如果使用props传递的话我们可以选择性的传递部分props或自己拼装新的props。
2. Context的Provier和Consumer是一一对应的,我们需要多少个不同数据结构的Context就需要创建多少个Context,这对于维护来说是不利的。
3. 当Provider传递的value发生变动时,Consumer组件就会更新,且更新不受到shouldComponentUpdate的影响,也就是说即使父组件在shouldComponentUpdate中设定了不更新,Consumer组件也会更新。
4. 其变动判断通过Object.is来判断,会导致不论使用了该Context的组件是否用到了Context中的变动的数据,组件都会更新。
5. Context无法存储数据,其数据的读取全部依靠于Provider组件的value值或者创建时的默认值,因此并不能像Redux那样将数据存储起来,再在页面中引用。建议将Context与State结合起来使用,将Context使用的变量维护在 State当中。当然你可以也将数据维护在Reudx中,这样就没有必要再使用Context了。
<br />
>[info]总的来说,context是React官方借鉴Redux理念的一个实践API,与Redux相比起来,在维护轻量级数据的问题上引用Context比引用Redux要轻量一些。但是Context在使用上也存在着自己的局限性,在实际项目中,复杂、重量级的数据还是应放在Redux中去维护,轻量的数据可以放在Context中去维护。