我们知道,react中父子组件之间的通信,父组件和子组件通信,可以通过props向子组件传递参数,子组件和父组件通信,则可以通过调用props传入的回调函数来向父组件传递参数
那么问题来了,如果是跨级组件之间的通信,如果通过props一层一层往下传,不仅逻辑会变得很不清晰,代码也会变得不好维护,这个时候,我们需要使用到react中的context来解决这个问题
首先我们来看一段代码
class Item extends Component {
static contextTypes = {
color: React.PropTypes.string,
};
render() {
const {value} = this.props;
return (
<li style={{background: this.context.color}}>
<span>{value}</span>
</li>
);
}
}
class List extends Component {
static childContextTypes = {
color: React.PropTypes.string,
};
getChildContext() {
return {
color: 'red',
};
}
render() {
const {list} = [{id:'1',text:1},{id:'1',text:2},{id:'1',text:3}]
return (
<div>
<ul>
{list.map((entry, index) => (
<Item key={entry.id} value={entry.text}/>
))}
</ul>
</div>
);
}
}
以上是一个如何通过context进行通信的示例,我们可以看到Item组件接收的color参数并不是通过props进行传递的,而是通过this.context获取的。
要实现这个功能,首先我们需要在父组件中显式的定义静态属性childContextTypes,并在属性中声明要传入的参数的类型,同时在父组件中实现getChildContext方法,这个方法的返回值,将会传入到父组件下面的任何一个子组件的context中,这样一来,父组件的所有级别的子组件都将可以跨级别获取到父组件的参数。
而当子组件需要使用父级及父级以上级别的组件传入的参数时,需要在子组件中显示定义静态属性contextTypes,并在属性中声明要使用的参数的类型,这样,子组件就可以在context中获取到跨级别传入的参数了。
这种方式在react-router中得到了应用,我们可以发现,在组件中写下了以下代码之后
static contextTypes = {
router: React.PropTypes.object.isRequired
}
我们就可以使用this.context.router.push等方法,进行路由跳转了,就是这个道理。
新的api出现
光阴似箭岁月如梭,来到了2018年,react发布了16.3版本,提供了新的context api,看上去更加的简洁,并且用了生产者消费者模式,看看下面的示例代码
const {Provider, Consumer} = React.createContext({
color: 'white'
});
class Item extends Component {
static contextTypes = {
color: React.PropTypes.string,
};
render() {
const {value} = this.props;
return (
<Consumer>
{context => (
<li style={{background: context.color}}>
<span>{value}</span>
</li>
)}
</Consumer>
);
}
}
class List extends Component {
render() {
const {list} = [{id: '1', text: 1}, {id: '1', text: 2}, {id: '1', text: 3}]
return (
<Provider value={{color: 'green'}}>
<div>
<ul>
{list.map((entry, index) => (
<Item key={entry.id} value={entry.text}/>
))}
</ul>
</div>
</Provider>
);
}
}
以上代码改写自前面的代码片段,用新的api重新实现,我们可以看到,上面的代码通过React.createContext创建出一个上下文:Context对象,然后这个Context对象又包含两个属性,一个叫Provider,另一个叫Consumer,这两个属性都是纯种的React组件。
之后我们只需要和上面代码片段一样,在父组件中运用Provider,在子组件中运用Consumer即可,Provider中通过value属性可以向Consumer传递参数,而Consumer的子组件则是一个函数,在这个子组件中定义一个函数,Consumer会向它传递一个context,这个context来自于Provider,达到通信的目的
和旧的api一样,这种传递参数的方式,支持跨多个级别的组件进行通信,可以用于从父组件向父组件一下的所有组件传递参数