[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)