**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