**React 组件之间的通讯是基于 props 的单向数据流,即父组件经过 props 向子组件传值,亦或是子组件执行传入的函数来更新父组件的state,这都知足了咱们大部分的使用场景。
那 React 在兄弟组件之间如何通讯呢?**react
通常地,对于兄弟组件之间的通讯,是经过它们共同的祖先组件进行的,即 Lifting State Up,官方文档也有介绍。组件一经过事件将状态变动通知它们共同的祖先组件,祖先组再将状态同步到组件二。ide
可是,若是组件之间嵌套的比较深,即便提高状态到共同父组件,再同步状态到相应的组件仍是须要一层一层的传递下去,可能会比较繁琐。函数
React 官方文档中介绍了 context 的用法, 在对应的场景中,context 就能够缩短父组件到子组件的属性传递路径。具体看例子:this
Container.jsxspa
import Parent from './Parent' import ChildOne from '../components/ChildOne' import ChildTwo from '../components/ChildTwo' export default class Container extends React.Component { constructor(props) { super(props); this.state = { value: '' } } changeValue = value => { this.setState({ value }) } getChildContext() { return { value: this.state.value, changeValue: this.changeValue } } render() { return ( <div> <Parent> <ChildOne /> </Parent> <Parent> <ChildTwo /> </Parent> </div> ) } } Container.childContextTypes = { value: PropTypes.string, changeValue: PropTypes.func }
Parent.jsxcode
import React from "react" const Parent = (props) => ( <div {...props} /> ) export default Parent
ChildOne.jsxcomponent
export default class ChildOne extends React.Component { handleChange = (e) => { const { changeValue } = this.context changeValue(e.target.value) } render() { return ( <div> 子组件一 <input onChange={this.handleChange} /> </div> ) } } ChildOne.contextTypes = { changeValue: PropTypes.func }
ChildTwo.jsx对象
export default class ChildTwo extends React.Component { render() { return ( <div> 子组件二 <p>{this.context.value}</p> </div> ) } } ChildTwo.contextTypes = { value: PropTypes.string }
在 Container.childContextTypes 中进行接口的声明,经过 getChildContext 返回更新后的state,在 Child.contextTypes 中声明要获取的接口,这样在子组件内部就能经过 this.context 获取到。经过 Context 这样一个中间对象,ChildOne 和 ChildTwo 就能够相互通讯了。blog
注意,React Context 也有一些局限性:接口
解决 ShouldComponentUpdate 与 Context 之间冲突的方案也是有的,例如使用 Redux 或者 Mobx 等全局单一状态管理。
这里抛砖引玉,介绍一种简单的解决 Context 不起做用的方法,它必须知足两个条件:
具体看代码:
// Theme stores the state of the current theme, and allows components class Theme { constructor(color) { this.color = color this.subscriptions = [] } setColor(color) { this.color = color this.subscriptions.forEach(f => f()) } subscribe(f) { this.subscriptions.push(f) } unsubscribe(f) { this.subscriptions = this.subscriptions.filter(m => m !== f) } } export default class ThemeProvider extends React.Component { constructor(p, c) { super(p, c) this.theme = new Theme(this.props.color) } componentWillReceiveProps(next) { this.theme.setColor(next.color) } getChildContext() { return {theme: this.theme} } handleChange = (e) => { const color = e.target.value this.theme.setColor(color) } render() { return ( <div> <select name="colors" onChange={this.handleChange}> <option value="red">red</option> <option value="green">green</option> <option value="blue">blue</option> </select> {this.props.children} </div> ) } } ThemeProvider.childContextTypes = { theme: PropTypes.object }
export default class ThemedText extends React.Component { state = { color: '' } componentDidMount() { this.updateTheme() this.context.theme.subscribe(this.updateTheme) } updateTheme = () => { this.setState({color: this.context.theme.color }) } componentWillUnmount() { this.context.theme.unsubscribe(this.updateTheme) } render() { return ( <div style={{color: this.state.color}}> {this.props.children} </div> ) } } ThemedText.contextTypes = { theme: PropTypes.object }
示例:
<ThemeProvider color="red"> <ThemedText> <p>xxxxxxxxxxxxxxxxxxxxxxx</p> </ThemedText> </ThemeProvider>
小结:
React Context 是嵌套层次较深的兄弟组件之间通讯的一种便捷方式,在某些使用场景做用是很强大的,因此须要谨慎使用。
最后推荐阅读这篇文章:How to safely use React context