[TOC]
# 父組件向子組件通信
react 的數據流是單向的,最常見的就是通過 `props` 由父組件向子組件傳值。
子組件中使用 `props` 屬性接收傳遞來的數據:
```
class Child extends Component {
render() {
{/*這里從props中拿到*/}
return <div> price: {this.props.price} </div>;
}
}
```
# 子組件向父組件通信
子組件向父組件通訊,同樣也需要父組件向子組件傳遞 props 進行通訊,只是父組件傳遞的,是作用域為父組件自身的函數,子組件調用該函數,將子組件想要傳遞的信息,作為參數,傳遞到父組件的作用域中。
父組件:
```
return (
<div>
<div>price: {this.state.price}</div>
{/* 向子組件中傳入一個函數 */}
<Child getPrice={this.getItemPrice.bind(this)} />
</div>
);
```
子組件:
```
class Child extends Component {
clickGoods(e) {
// 在此函數中傳入值
this.props.getPrice(e);
}
render() {
return (
<div>
<button onClick={this.clickGoods.bind(this, 100)}>goods1</button>
<button onClick={this.clickGoods.bind(this, 1000)}>goods2</button>
</div>
);
}
}
```
# 發布者與訂閱者模式(context)
React 的 props 都是由父組件傳遞給子組件的,一旦遇到孫組件,就需要一層層的傳遞下去。而 context 提供了一種組件之間通訊的新的方式(16.3版本之后),可以共享一些數據,其它的組件都能從 context 中讀取數據(類似于有個數據源,組件可以訂閱這個數據源)。
## `React.createContext()`方法
我們可以使用createContext來創建一個context,它可以接收一個變量或者對象做為參數(當對象為參數的時候,react使用object.is()去比較,有些影響性能)。這個傳入的值做為context的默認值
~~~
const PriceContext = React.createContext('price')
~~~
這樣就創建了一個Context
## `Provider`組件
Provider就是用來創建數據源的。它是給所有的子組件提供數據源的跟組件。它接受一個value作為props,用來傳遞值,它會改變context的默認值。一個provider可以包含多個Consumer組件。如果Provider組件嵌套的話,
~~~
<PriceContext.Provider value={100}>
</PriceContext.Provider>
~~~
## `Consumer`組件
Consumer表示接受數據的組件,它接受一個函數做為子元素。這個函數會接收context傳遞的值,返回一個react的組件。Consumer組件必須包含在Provider里面。
~~~
<PriceContext.Consumer>
{ /*這里是一個函數*/ }
{
price => <div>price:{price}</div>
}
</PriceContext.Consumer>
~~~
# 非嵌套組件間通信
即沒有任何包含關系的組件,包括兄弟組件以及不在同一個父級中的非兄弟組件。
1. 利用二者共同父組件的 context 對象通信 (兄弟組件)
```
import React from 'react';
import ReactDOM from 'react-dom';
import 'font-awesome/css/font-awesome.min.css';
import './index.css';
import './index.scss';
class Child_1 extends React.Component {
constructor(props) {
super(props);
}
handleClick() {
this.props.changeChild_2Color('blue');
}
render() {
return (
<div>
<h1>Child_1:{this.props.bgColor}</h1>
<p>
<button onClick={(e) => {
this.handleClick(e)
}}>改變Child_2背景色bgColor
</button>
</p>
</div>
);
}
}
class Child_2 extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div style={{backgroundColor: this.props.bgColor}}>
<h1>child_2背景色:{this.props.bgColor}</h1>
</div>
);
}
}
class Father extends React.Component {
constructor(props) {
console.log('constructor---props:', props);
super(props);
this.state = {
child_2BgColor: '#999'
};
console.log('constructor---props:', props);
}
// 子組件調用該函數改變 state ,所以用到的子組件會被更新渲染!
onChild_2BgColorChange(color) {
this.setState({
child_2BgColor: color
});
}
render(props) {
console.log('render---props:', props);
return (
<div>
<Child_1 changeChild_2Color={(color) => {
this.onChild_2BgColorChange(color)
}}/>
<Child_2 bgColor={this.state.child_2BgColor}/>
</div>
)
}
}
class App extends React.Component {
render() {
return (
<div className="">
<Father>
<p><span>App Span</span></p>
<p><a href="">link</a></p>
</Father>
<hr/>
</div>
);
}
}
ReactDOM.render(
<div>
{/*<Component></Component>*/}
<App></App>
</div>,
document.getElementById('app')
);
```
使用二者共同父級進行中轉**會增加子組件和父組件間的耦合度**,如果組件層次比較深,找到二者共同父組件會相對麻煩。
2. 使用自定義事件 (流行的庫之類)
發布者-訂閱者模式也叫觀察者模式,大量使用在各類框架類庫的設計中。發布者發布事件,訂閱者監聽事件并作出反應,我們可以引入一小個模塊,使用觀察者模式進行改造。
在componentDidMount事件中,如果組件掛載完成,訂閱事件;
在組件卸載的時候,在componentwillUnmount事件中取消對事件的訂閱。
例如:使用自定義事件方式需要使用 `events` 庫:
```
npm install -S events
```
# 借助其他方式
還可以使用 `localstorage`,狀態管理庫(redux|mobx),或者是一些訂閱發布的庫 events 、[pubsub-js](https://npm.io/package/pubsub-js)。
# 參考
> [React組件間通信](https://juejin.im/post/5cbea2535188250a52246717)