yanyue404/blog

React组件通信

Opened this issue · 0 comments

组件通信

在这里只讲 React 组件与组件本身的通信,组件通信主要分为三个部分:

  • 父组件向子组件通信:父组件向子组件传参或者是父组件调用子组件的方法
  • 子组件向父组件通信:子组件向父组件传参或者是子组件调用父组件的方法
  • 兄弟组件通信:兄弟组件之间相互传参或调用 建议不要有太深的的嵌套关系

父传子

  • 父组件向子组件传参
    父组件向子组件参可以在子组件上绑定自定义属性,或者将父组件内部 state 的值进行绑定为子组件的属性值进行传递。

  • 父组件调用子组件的方法
    在子组件上绑定 ref 属性,并在子组件内部定义方法(可接受参数),在父组件内部使用this.refs.自定义属性值.函数方法名,进行调用,可传递参数。

  • components/parentToChild/Parent.js

// 父组件

import React from 'react';
import Child from './child';

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
  }
  handleChange(e) {
    this.value = e.target.value;
    // 调用子组件的方法
    this.refs.c1.changeChild(this.value);
  }
  handleClick() {
    this.setState({
      value: this.value
    });
  }

  render() {
    return (
      <div>
        我是parent
        <input onChange={this.handleChange.bind(this)} />
        <button className="button" onClick={this.handleClick.bind(this)}>
          通知
        </button>
        <div>
          <Child ref="c1" value={this.state.value} />
        </div>
      </div>
    );
  }
}

export default Parent;
  • components/parentToChild/child.js
// 子组件
import React from 'react';

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ''
    };
  }

  changeChild(text) {
    this.setState({
      text: text
    });
  }
  render() {
    return (
      <div>
        <p>我是Child,接受来自父组件的传参:{this.props.value}</p>
        <br />
        <p>我是child,来自父组件对组件内部函数的的调用:{this.state.text}</p>
      </div>
    );
  }
}

export default Child;
  • App.js
import React from 'react';
import './App.css';
import ParentToChild from './components/parentToChild/Parent';

class App extends React.Component {
  render() {
    return (
      <div className="App">
        {/* 父传子 */}
        {/* <ParentToChild /> */}
      </div>
    );
  }
}

export default App;

子传父

子组件向父组件传值,在被调用的子组件上先定义回调函数,再来单独的子组件上定义新的函数,通过 props 获取 callback 函数进行值传递.

这里的 state 可以分别定义在父组件或组子组件上

  • components/sonToFather/Father.js
// Father

import React from 'react';
import Son from './son';
class Father extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
  }

  setValue(param) {
    this.setState({
      value: param
    });
  }

  render() {
    return (
      <div>
        <Son setValue={this.setValue.bind(this)}></Son>
        <p>我是Father,接受子组件的传参:{this.state.value}</p>
      </div>
    );
  }
}

export default Father;
  • components/sonToFather/son.js
// son
import React from 'react';
class Son extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ''
    };
  }
  handleChange(e) {
    this.props.setValue(e.target.value);
  }

  render() {
    return (
      <div>
        我是Son
        <input onChange={this.handleChange.bind(this)} />
        <br />
      </div>
    );
  }
}

export default Son;
  • App.js
import React from 'react';
import './App.css';
import SonToFather from './components/sonToFather/Father';
class App extends React.Component {
  render() {
    return (
      <div className="App">
        {/* 子传父 */}
        <SonToFather />
      </div>
    );
  }
}

export default App;

兄弟组件

利用共同的父组件

先将两个子组件child1child2的参数传递给父组件,再由分发参数,通过refs寻找函数方法的方式进行函数调用传参。

// Parent

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
  }

  setValue2(param) {
    this.refs.c1.changeState(param);
    this.setState({
      value: param
    });
  }

  setValue1(param) {
    this.refs.c2.changeState(param);
    this.setState({
      value: param
    });
  }

  render() {
    return (
      <div>
        <p>我是Parent,接受子组件的传参:{this.state.value}</p>

        <Child1 ref="c1" setValue={this.setValue1.bind(this)}></Child1>
        <Child2 ref="c2" setValue={this.setValue2.bind(this)}></Child2>
      </div>
    );
  }
}
// Child1

class Child1 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ''
    };
  }
  handleChange(e) {
    this.props.setValue(e.target.value);
  }
  changeState(text) {
    this.setState({ text: text });
  }

  render() {
    return (
      <div>
        我是child1
        <input onChange={this.handleChange.bind(this)} />
        <p>来自子组件2的调用: {this.state.text}</p>
        <br />
      </div>
    );
  }
}
// Child2
class Child2 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: ''
    };
  }
  handleChange(e) {
    this.props.setValue(e.target.value);
  }
  changeState(text) {
    this.setState({ text: text });
  }

  render() {
    return (
      <div>
        我是child1
        <input onChange={this.handleChange.bind(this)} />
        <p>来自子组件1的调用: {this.state.text}</p>
        <br />
      </div>
    );
  }
}

利用 Context

// 父组件
var Parent = React.createClass({
  getChildContext: function() {
    return {
      changeChildren1: function(text) {
        this.refs.cp1.changeState(text);
      }.bind(this),
      changeChildren2: function(text) {
        this.refs.cp2.changeState(text);
      }.bind(this)
    };
  },
  childContextTypes: {
    changeChildren1: React.PropTypes.func.isRequired,
    changeChildren2: React.PropTypes.func.isRequired
  },
  render: function() {
    return (
      <div>
        <Children1 ref="cp1" />
        <Children2 ref="cp2" />
      </div>
    );
  }
});
//子组件1
var Children1 = React.createClass({
  getInitialState: function() {
    return {
      text: ''
    };
  },
  contextTypes: {
    changeChildren2: React.PropTypes.func.isRequired
  },
  changeState: function(text) {
    this.setState({ text: text });
  },
  //输入事件
  change: function(event) {
    //调用子组件的方法
    this.context.changeChildren2(event.target.value);
  },
  render: function() {
    return (
      <div>
        <p>
          <label>子组件1</label>
          <input type="text" onChange={this.change} />
        </p>
        <p>来自子组件2的调用-{this.state.text}</p>
      </div>
    );
  }
});
//子组件2
var Children2 = React.createClass({
  getInitialState: function() {
    return {
      text: ''
    };
  },
  contextTypes: {
    changeChildren1: React.PropTypes.func.isRequired
  },
  changeState: function(text) {
    this.setState({ text: text });
  },
  //输入事件
  change: function(event) {
    //调用子组件的方法
    this.context.changeChildren1(event.target.value);
  },
  render: function() {
    return (
      <div>
        <p>
          <label>子组件2</label>
          <input type="text" onChange={this.change} />
        </p>
        <p>来自子组件1的调用-{this.state.text}</p>
      </div>
    );
  }
});

发布订阅

在 componentDidMount 事件中,如果组件挂载完成,再订阅事件;在组件卸载的时候,在 componentWillUnmount 事件中取消事件的订阅;以常用的发布/订阅模式举例,借用 Node.js Events 模块的浏览器版实现

先引入 node events 模块

yarn add events

在 src 下新建一个 util 目录里面建一个 events.js

import { EventEmitter } from 'events';
export default new EventEmitter();
  • components/eventCenter/List1.js
import React, { Component } from 'react';
import emitter from '../../util/events';
class List1 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'List1'
    };
  }
  componentDidMount() {
    // 组件装载完成以后声明一个自定义事件
    this.eventEmitter = emitter.addListener('changeMessage', message => {
      this.setState({
        message
      });
    });
  }
  componentWillUnmount() {
    emitter.removeListener(this.eventEmitter);
  }
  render() {
    return (
      <div>
        {this.state.message} {this.props.msg}
      </div>
    );
  }
}
export default List1;
  • components/eventCenter/List2.js
import React, { Component } from 'react';
import emitter from '../../util/events';
class List2 extends Component {
  handleClick = message => {
    emitter.emit('changeMessage', message);
  };
  handleClick2 = message => {
    emitter.emit('changeMessageApp', message);
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this, 'List2')}>
          点击我改变List1组件中显示信息
        </button>
        <button onClick={this.handleClick2.bind(this, 'List2App')}>
          点击我改变App组件中显示信息
        </button>
      </div>
    );
  }
}

export default List2;

APP.js

import React from 'react';
import './App.css';
import List1 from './components/eventCenter/List1';
import List2 from './components/eventCenter/List2';
import emitter from './util/events';
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'msgFromApp'
    };
  }
  componentDidMount() {
    // 组件装载完成以后声明一个自定义事件
    this.eventEmitter = emitter.addListener('changeMessageApp', message => {
      this.setState({
        message
      });
    });
  }
  render() {
    return (
      <div className="App">
        <List1 msg={this.state.message} />
        <List2 />
      </div>
    );
  }
}

export default App;

此方法还支持子父组件的传值,但考虑到消息中心的可维护性,易维护性,采用以下的方法

主流通信库

Redux || Mobx

参考文章