世界上最伟大的投资就是投资自己的教育

首页React
随风 · 练气

react 组件间的通信方法

随风发布于4340 次阅读

1. 介绍

react 推荐的开发方法,是高度组件化,就是一个大应用,切分成不同的组件,再把它们组合在一起。

这样就会能以避免到不同的组件之间的交流,比如传递数据,而这样又有好多种情况,可以从父组件或从子组件传递是最简单的,有些时候你会发现,把数据从一个组件传递到另一个组件是一件好复杂的事,这可能是另外的情况。

我把组件的数据传递分为四种情况来一一讲述。

2. 父子间

这种方式是最简单,也是最常见的。

父组件要把数据传递给子组件,就通过props传过去。子组件可以通过this.props方法来获得传递过来的数据,这种是最基本,很常见的,几乎每个写 react 的人都会用到吧。

然而 react 的数据流向是单向的,如果子组件要传递数据给父组件就要一些技巧,不过这种情况也很常见。

就是首先父组件传递一个方法名给子组件,这个方法必须先在父组件定义好,比如叫父组件叫ElementGroup,子组件叫Element,这时候可以在渲染子组件的时候,传递一个方法,叫deleteElement

<Element handleDeleteElement={ this.deleteElement } />

deleteElement方法已经在父组件定义好了。

deleteElement(element) {
  const index = this.state.elements.indexOf(element)
  const elements = ReactAddonsUpdate(this.state.elements, { $splice: [[index, 1]] })
  this.replaceState({elements: elements})
}

这个时候,在子组件Element就可以通过this.props.handleDeleteElement方法来调动,并且可以把数据传给父组件。

比如

this.props.handleDeleteElement(this.props.meta_element)

这样就可以轻易把this.props.meta_element传递给父组件的deleteElement方法的element参数。

下面摘自reactjs-two-components-communicating的一段代码:

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    var content;
    if (displayedItems.length > 0) {
      var items = displayedItems.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

React.renderComponent(<List />, document.body);

可以在这里具体体会上述的思想。

3. 兄弟间

兄弟间的组件,我们有一个共同的特点,就是父级组件,所以它们的数据传递主要是靠父级组件作为中间桥梁。

下面用具体的代码体会:

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  render: function() {
    var content;
    if (this.props.items.length > 0) {
      var items = this.props.items.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }
    return (
      <div className="results">
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

var ListContainer = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <List items={displayedItems} />
      </div>
    );
  }
});

React.renderComponent(<ListContainer />, document.body);

4. 嵌套太深的继承关系

嵌套太深,比如就算是同级的,有好多层嵌套,这个时候如果还用上面的方法,那就是噩梦了,写起来和管理起来都会令人头疼。

下面来说说解决此问题的几种方法。

5. context(上下文)

context这个可以让任意组件进行数据交换,这也是 react 提供的方法,不过不建议用,我觉得也不需要去懂,只知道这个可以用。

6. 事件模式

这种模式有点类似于 backbone 的 listen_to,也有点类似于订阅消费模式,如果用这种模式,可以让任意的组件互传数据的,但是缺点可能就是会写些侵入式代码。

比如你可以让任何组件都能使用类似消息通知这样的功能,可以先在最顶级组件上使用订阅功能。

EventEmitter.subscribe('alert', function(info){
  this.showAlert(info)
}.bind(this))

在任何需要使用这个功能的组件上就可以这样:

EventEmitter.dispatch('alert', '操作成功')

你可以使用类似EventEmitter这种库来实现这样的功能。

也可以自己实现一个简单版本的:

export default {
  _events: {},
  dispatch: function (event, data) {
    if (!this._events[event]) return // no one is listening to this event
    for (let i = 0; i < this._events[event].length; i++)
      this._events[event][i](data)
  },
  subscribe: function (event, callback) {
    if (!this._events[event]) this._events[event] = [] // new event
    this._events[event].push(callback)
  },
  unSubscribe: function(event){
    if(this._events && this._events[event]) {
      delete this._events[event]
    }
  }
}

有兴趣的可以看下这个库: https://github.com/facebook/emitter

7. redux

如果你使用过 redux,或了解过它的理念,你肯定知道为什么 redux,能解决这个组件交流的问题。

// 想要绑定什么就传过来reducer中的state, 比如state.header
const mapStateToProps = (state, ownProps) => ({
  response: state.header
})

const mapDispatchToProps = (dispatch, ownProps) => ({
  actions: bindActionCreators(HeaderActions, dispatch)
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Header)

8. 总结

我是这样觉得的,如果你使用了 redux,那就不用在乎组件的数据交流了,它本身可以解决,如果没有使用的话,可以考虑下事件模式。

本站文章均为原创内容,如需转载请注明出处,谢谢。

1 条回复
喜欢
统计信息
    学员: 29915
    视频数量: 1996
    文章数量: 526

© 汕尾市求知科技有限公司 | Rails365 Gitlab | 知乎 | b 站 | csdn

粤公网安备 44152102000088号粤公网安备 44152102000088号 | 粤ICP备19038915号

Top