Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React组件通信 #28

Open
yanyue404 opened this issue May 24, 2018 · 0 comments
Open

React组件通信 #28

yanyue404 opened this issue May 24, 2018 · 0 comments

Comments

@yanyue404
Copy link
Owner

yanyue404 commented May 24, 2018

组件通信

在这里只讲 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

参考文章

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant