React组件间通信
这周重点学习React的组件间通信。
1. 组件间通信
React的组件间通信可分为四种:
- 父组件向子组件通信
- 子组件向父组件通信
- 跨级组件间通信
- 无嵌套关系组件间通信
1.1.父组件向子组件通信
因为React的数据流是单向流动的,所以父组件向子组件通信也是最常见的通信方式。父组件通过props向子组件传递数据。
举一个简单的例子:
子组件中:
import React from 'react';
export default function Child({ name }) {
return <h1>Hello, {name}</h1>;
}
父组件中:
import React, { Component } from 'react';
import Child from './Child';
class Parent extends Component {
render() {
return (
<div>
<Child name="Sara" />
</div>
);
}
}
export default Parent;
上述例子中父组件向子组件传递了name属性,在子组件中就可以用this.props.name的方式获取从父组件传来的name值。
1.2.子组件向父组件通信
子组件向父组件通信主要有两种方法:
1.利用回调函数:子组件更新组件状态,通过回调函数的方式传递给父组件。
2.利用自定义事件机制:这种方法更加通用广泛,加入事件机制可以简化组件API。
举个例子:
父组件中:定义回调函数
import React, { Component } from 'react';
import Child from './child.js';
class App extends Component {
constructor(props){
super(props);
this.state = {
msg: '父组件初始msg'
}
}
//父组件回调函数,更新state,进而更新父组件。
callback=(msg)=>{
this.setState({msg});
}
render() {
return (
<div className="App">
<p>子组件传值实验: {this.state.msg}</p>
<Child callback={this.callback} ></Child>
</div>
);
}
}
export default App;
子组件中:调用父组件传来的回调函数,并把值通过回调函数传递给父组件。
import React from "react";
class Child extends React.Component{
constructor(props){
super(props);
this.state={
msg: '子组件msg传值'
}
}
//通过props调用回调函数传值
trans=()=>{
this.props.callback(this.state.msg);
}
render(){
return(
<div>
<button onClick={this.trans}>激发trans事件,传值给父组件</button>
</div>
)
}
}
export default Child;
上述例子中,在父组件中定义了一个回调函数,可以实时更新传入的参数为this.state中的值,然后在子组件中通过this.props.回调函数调用它,并把自己的this.state中的值传给这个回调函数,从而达到子组件向父组件传值的作用。
1.3.跨级组件间通信
在React中,可以使用context来实现跨级父子组件间的通信。context提供了一种组件之间共享数据的方式,可以避免数据在组件树上逐层传递,但大部分情况下,不推荐使用context,因为它属于全局变量,容易引起结构混乱。如果真的需要使用,建议写成高阶组件来实现。
注:
Context API的使用基于生产者消费者模式。
生产者一方,通过组件静态属性childContextTypes声明,然后通过实例方法getChildContext()创建Context对象。
消费者一方,通过组件静态属性contextTypes申请要用到的Context属性,然后通过实例的context访问Context的属性。
举个例子:
生产者:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import CppComponent from './Cpp.js';
class App extends Component {
constructor(props){
super(props);
}
//Context生产者,通过静态属性childContextTypes声明提供给子组件的Context对象的属性,
static childContextTypes = {
propA: PropTypes.string
}
//实例getChildContext方法,返回Context对象
getChildContext () {
return {
propA: 'propA'
}
}
render() {
return <BppComponent />
}
}
class BppComponent extends React.Component {
render () {
return <CppComponent />
}
}
export default App;
消费者:
import React, { Component } from 'react';
import PropTypes from 'prop-types'
/**
* 第三层有A(生产者)层直接传递数据到此层C(消费者)
*/
class CppComponent extends React.Component {
//子组件需要通过一个静态属性contextTypes声明后,才可以访问父组件Context对象的属性
static contextTypes = {
propA: PropTypes.string
}
render () {
return(
<div>
<p>从生产者传递过来的属性A:{this.context.propA}</p>
</div>
)
}
}
export default CppComponent;
上面的例子其实就运用了高阶组件,高阶组件说通俗点就是一个函数,它接受一个React组件作为参数输入,然后输出一个新的React组件。
1.4.无嵌套关系组件间通信
非嵌套组件: 就是没有任何包含关系的组件,包括兄弟组件以及不再同一个父级的非兄弟组件。
使用事件订阅,即一个发布者,一个或多个订阅者。
这里我们借用Node.js Events 模块的浏览器版实现。
- 安装event
npm install event -save
- 新建Evt.js, 创建EventEmitter 实例
import { EventEmitter } from 'events';
export default new EventEmitter();
- 发布者通过emit事件触发方法,发布订阅消息给订阅者,把 EventEmitter 实例输出到各组件中使用;
订阅者通过emitter.addListener(事件名称,函数名)方法,进行事件监听(订阅);通过emitter.removeListener(事件名称,函数名)方法 ,进行事件销毁(取消订阅)
举例:
发布者:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Custom1 from './Custom1.js';
import Custom2 from './Custom2.js';
import emitter from './Evt.js';
class App extends Component {
constructor(){
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
//emit事件触发方法,通过事件名称找对应的事件处理函callCustom,将事件处理函数作为参数传入
emitter.emit('callCustom', 'Hello 我来发消息了');
}
render() {
return(
<div>
<br/>
<button onClick = {this.handleClick}>点击发布事件</button>
<Custom1 />
<Custom2 />
</div>
)
}
}
export default App;
订阅者1 Custom1:
import React from 'react';
import ReactDOM from 'react-dom';
import emitter from './Evt.js';
class Custom1 extends React.Component {
constructor(){
super();
this.state= {
msg:''
}
}
componentDidMount () { //在组件挂载完成后声明一个自定义事件
emitter.addListener('callCustom', (msg) => {
this.setState({
msg: 'Custom1收到消息--'+msg
});
})
}
componentWillUnmount () { //组件销毁前移除事件监听
emitter.removeListener('callCustom', (msg) => {
this.setState({
msg: 'Custom1即将销毁此消息--'+ msg
});
})
}
//订阅者1消息显示
render () {
return(<p style={{color:'red'}}>
{this.state.msg}
</p>)
}
}
export default Custom1;
订阅者2 Custom2:
import React from 'react';
import ReactDOM from 'react-dom';
import emitter from './Evt.js';
class Custom2 extends React.Component {
constructor(){
super();
this.state= {
msg:''
}
}
componentDidMount () { //在组件挂载完成后声明一个自定义事件
emitter.addListener('callCustom', (msg) => {
this.setState({
msg: 'Custom2收到消息--'+msg
})
})
}
componentWillUnmount () { //组件销毁前移除事件监听
emitter.removeListener('callCustom', (msg) => {
this.setState({
msg: 'Custom2即将销毁此消息--'+ msg
})
})
}
//订阅者2消息显示
render () {
return(<p style={{color:'blue'}}>{this.state.msg}</p>)
}
}
export default Custom2;
最终预览效果如下图:
![](https://img.haomeiwen.com/i16775500/7aac794a401df90e.png)