纵横研究院React技术专题社区

React组件间通信

2019-06-08  本文已影响13人  爱吃芋圆的小w

这周重点学习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 模块的浏览器版实现。

  1. 安装event
   npm install event -save
  1. 新建Evt.js, 创建EventEmitter 实例
   import { EventEmitter } from 'events'; 
   export default new EventEmitter();
  1. 发布者通过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;

最终预览效果如下图:


上一篇 下一篇

猜你喜欢

热点阅读