背景介绍
假设有以下组件结构,并且只有D组件会用到A组件内的数据,那么就需要将props传递很多层,这样不仅书写起来很繁琐同时还会为夹在中间的组件引入不必要的 props
<A>
<B>
<C>
<D />
</C>
</B>
</A>
Context API解决这类问题就很好用,很多流行框架(例如redux,mobx等)都在使用它。但是在React16.3.0之前,官方还是不推荐我们使用。这篇文章说明了使用旧版Context会出现的问题以及解决办法
但是React16.3.0发布了更加高效、稳定的Context API
Context API
Context API主要由以下几个部分:
- React.createContext:创建一个 Context 对象,包含两个属性名为Provider和Consumer的组件
const MyContext = React.createContext(defaultValue);
- Provider组件:它接受一个value属性,将其传给需要使用context值的子组件,value属性的值可以是任何JS中的数据类型
<MyContext.Provider value={someValue}>
- Consumer 组件:可以在Provider组件内部的任何一层使用,其子元素是一个参数等于Provider组件提供的value,返回值是一个 React节点的函数
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
举个简单栗子:
// context.js
import { createContext } from 'react';
const defaultVal = 'defaultVal'
export default createContext(defaultVal);
// app component
import MyContext from './context';
class App extends React.Component {
render() {
return (
<MyContext.Provider value={'someValue'}>
<div>Context</div>
<Tab />
</MyContext.Provider>
);
}
}
// tab component
const Tab = () => (
<div>
<List />
</div>
);
// list component
import MyContext from './context';
const List = props => (
<MyContext.Consumer>
{
value => (
<div>{value}</div>
)
}
</MyContext.Consumer>
);
- Class.contextType:将创建的Context对象分配给class的contextType属性,这样就可以在class组件内使用this.context使用Context(函数组件不支持),修改上述例子的app组件:
class App extends React.Component {
render() {
return (
<MyContext.Provider value={this.context}>
<div>Context</div>
<Tab />
</MyContext.Provider>
);
}
}
App.contextType = MyContext;
或者使用babel插件,使用static来初始化contextType :
class App extends React.Component {
// use babel plugins
static contextType = MyContext;
render() {
return (
<MyContext.Provider value={this.context}>
<div>Context</div>
<Tab />
</MyContext.Provider>
);
}
}
注意:当Provider组件的value值是对象时,每一次Provider重新渲染时,其下面所有的consumers组件会重新渲染,因为value属性总是被赋值为新的对象。可以通过将value的值提升到state里来防止这种情况
代码示例:https://github.com/MirrorHuang/react16-demo