站点图标 久久日记本

React事件传递

React事件传递

目录

*1.上节回顾

*2.成绩搜索分数单据

*3.过滤器控件

*4.图表控件

*5.合并控件

*6.事件传递

1.上节回顾

css样式分离成单独的文件,

列表数据展示

引入bootstrap

2.成绩搜索分数单

需求:一个成绩列表,可以根据标题(分数,日期)筛选列表。

所以这里可以分为筛选组件和图表组件

简化一下解决方案,我们要实现一个类似于下面的页面。

效果图:

文件夹结构如下:

project
|
|--components
|       |
|       |--ArticleScore
|       |         |--filter
|       |         |    |--MainFilter.jsx (过滤器控件 父组件)
|       |         |    |--TitleFilter.jsx (标题过滤器 子组件)
|       |         |
|       |         |--table
|       |         |    |--MainTable.jsx (图表 父组件)
|       |         |    |--ScoreHeader.jsx (图表 header 子组件)
|       |         |    |--ScoreRow.jsx (图表 table 子组件)
|       |         | 
|       |         |--ArticleScore.jsx (过滤器控件+图表控件 的 父组件)
|       |         |--data.json (数据来源)
|       |
|       |--Index.jsx (主 父组件)
|
|--css(这里并未使用css样式)
|   
|    
|--lib
|   |--bootstrap(引入bootstrap,这里并未使用bootstrap)
|   |--common
|         |
|         |--dateformat.jsx (格式化日期)
|
|--index.html(首页)
|--package.json (包)
|--webpack.config.js (webpack配置)

在这里我们将专注解决 components 文件夹发生的事情。

3.过滤器控件

TitleFilter.jsx (标题过滤器 子组件)

'use strict'
module.exports = React.createClass({
  displayName: 'TitleFilter',
  render: function () {
    // 默认值 defaultValue
    return (
      <div>
        <span>按标题筛选</span>
        <input type="text" defaultValue={this.props.titleFilter} onChange={this.props.onTitleChanged} />
        <span>{this.props.titleFilter}</span>
      </div>
    )
  }
});

我们用一个父组件来包裹它(基于此,我们可以单独分出更多的子过滤器组件,这里只列举标题事例)

onChange 向父类传递一个数据textchange事件来实现数据筛选

MainFilter.jsx (过滤器控件 父组件)

'use strict'
var TitleFilter = require('./TitleFilter');
module.exports = React.createClass({
  displayName: 'MainFilter',
  getInitialState: function () {
    console.log();
    return null;
  },
  render: function () {
    return (
      <div>
        <div>评分</div>
        <TitleFilter titleFilter={this.props.titleFilter} dataSource={this.props.dataSource} onTitleChanged={this.props.onTitleParentChanged}></TitleFilter>
      </div>
    )
  }
});
4.图表控件

ScoreHeader.jsx (图表 header 子组件)

'use strict'
module.exports = React.createClass({
  displayName: 'ScoreHeader',
  render: function () {
    return (
      <thead>
        <tr>
          <th>编号</th>
          <th>标题</th>
          <th>作者</th>
          <th>性别</th>
          <th>得分</th>
          <th>发布日期</th>
        </tr>
      </thead>
    )
  }
});

ScoreRow.jsx (图表 table 子组件)

'use strict'
var DateFormat = require('../../../lib/common/dateformat');
module.exports = React.createClass({
  displayName: 'ScoreRow',
  getInitialState: function () {
    return null;
  },
  render: function () {
    var scoreshtml = this.props.dataSource.map(function (obj) {
      return (
        // react-with-addons.js:5283Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using <tbody>. See https://fb.me/react-warning-keys for more information. in tr
        <tr key={obj.id}>
          <td>{obj.id}</td>
          <td>{obj.title}</td>
          <td>{obj.author}</td>
          <td>{obj.gender}</td>
          <td>{obj.score}</td>
          <td>{DateFormat(obj.date, 'yyyy-MM-dd')}</td>
        </tr>
      )
    });
    return <tbody>{scoreshtml}</tbody>
  }
});

同样用一个父组件来组装上面两个:

MainTable.jsx (图表 父组件)

'use strict'
// var React = require('react');
var ScoreHeader = require('./ScoreHeader');
var ScoreTable = require('./ScoreRow');
module.exports = React.createClass({
  displayName: 'MainTable',
  render: function () {
    return (
      <table className="table table-striped">
        <ScoreHeader></ScoreHeader>
        <ScoreTable dataSource={this.props.dataSource}></ScoreTable>
      </table>
    )
  }
});

5.合并控件

组合上述控件,确定一下功能,

Filter 接收关键字输入,触发textchange事件,

Table 接收Filter的textchange事件的触发,更新数据,重新渲染

Filter和Table的分离导致我们不能很好的触发事件,所以我们需要一个父组件来包裹它们,使他们能够很好的传递。

所以我们构建成一个 ArticleScore.jsx 组件来实现:

'use strict'
var MainFilter = require('./filter/MainFilter.jsx');
var MainTable = require('./table/MainTable');
module.exports = React.createClass({
  displayName: 'ArticleScore',
  getInitialState: function () {
    return {
      // 在getInitialState加载ajax请求会导致后面子组件调用时需要回调才能获取这里的数据
      // dataSource: $.getJSON('./components/ArticleScore/data.json', function (data) {
      //   return data.responseJSON;
      // }),
      dataSourceBase: [],//ajax来的原始数据
      dataSource: [],//绑定的数据
      titleFilter: '重生'//过滤器关键字
    }
  },
  componentDidMount: function () {
    $.getJSON('./components/ArticleScore/data.json', function (result) {
      if (this.isMounted()) {
        this.setState({
          dataSourceBase: result,
          dataSource: result
        });
      }
    }.bind(this));
  },
  // 从子组件逐级上传上来的更新事件
  // 子组件 层次2 TitleFilter.jsx(onChange={this.props.onTitleChanged}) 调用父组件(子组件 层次1) onTitleChanged 方法
  // 子组件 层次1 MainFilter.jsx(onTitleChanged={this.props.onTitleParentChanged}) 调用父组件(父组件 层次0) onTitleParentChanged 方法
  // 父组件 层次0 ArticleScore.jsx(onTitleParentChanged={this.onTitleParentChanged}) 调用本级 onTitleParentChanged 方法 
  onTitleParentChanged: function (event) {
    var keyword = event.target.value;
    var scores = [];
    this.state.dataSourceBase.map(function (obj) {
      if (obj.title.indexOf(keyword) >= 0) {
        scores.push(obj);
      }
    });
    this.setState({
      dataSource: scores
    });
  },
  render: function () {
    return (
      <div>
        <div>评分</div>
        <MainFilter titleFilter={this.state.titleFilter} onTitleParentChanged={this.onTitleParentChanged}></MainFilter>
        <MainTable dataSource={this.state.dataSource}></MainTable>
      </div>
    )
  }
});
6.事件传递

在5中, 事件这样传递:


|--components | | | |--ArticleScore | | |--filter | | | |--MainFilter.jsx | | | |2.定义 onTitleChanged 事件: onTitleChanged={this.props.onTitleParentChanged} 调用父类的 onTitleParentChanged 事件 | | | | | | | |--TitleFilter.jsx | | | |1.文本输入框事件 onChange: onChange={this.props.onTitleChanged} 调用父类的 onTitleChanged 事件 (props是父类的) | | | | | | | | | |--table | | | |--MainTable.jsx (图表 父组件) | | | |--ScoreHeader.jsx (图表 header 子组件) | | | |--ScoreRow.jsx (图表 table 子组件) | | | | | |--ArticleScore.jsx (过滤器控件+图表控件 的 父组件) | | |3. <MainFilter titleFilter={this.state.titleFilter} onTitleParentChanged={this.onTitleParentChanged}></MainFilter> | | |在Index.jsx父组件中定义一个onTitleParentChanged 事件, onTitleParentChanged={this.onTitleParentChanged} | | | | | | | | |--data.json (数据来源) | | | |--Index.jsx (主 父组件) | | | |

在 ArticleScore.jsx 中 ,componentDidMount的时候初始化ajax请求得到数据,存入state中:

  componentDidMount: function () {
    $.getJSON('./components/ArticleScore/data.json', function (result) {
      if (this.isMounted()) {
        this.setState({
          dataSourceBase: result,
          dataSource: result
        });
      }
    }.bind(this));
  }

关于组件的生命周期,你可以在这篇文章中获得:

生命周期方法
React的组件拥有简洁的生命周期API,它仅仅提供你所需要的方法,而不会去最求全面。
实例化:
一个实例出吃被穿件时所调用的生命周期方法与其他哥哥后续实例被创建所调用的方法略有不同。当你首次使用一个组建类时,会看到下面这些方法依次被调用:
getDefaultProps
getInitialState
componentWillMount
render
ComponentDidMOunt

对于该组件类所有后续应用,你将会看到下面的方法依次被调用。注意:gerDefaultProps方法不在列表中。
getInitialState
componentWillMount
render
componentDidMount

存在期:
随着应用状态的改变,以及组件逐渐受到影响,你将会看到下面的方法一次被调用:
componentwillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

销毁&清理期:
最后,当该组件被使用完成后,componentWillUnmount方法会被调用,目的是给这个实例提供清理自身的机会。

onTitleParentChanged事件将更新state,组件table数据将被自动刷新

  onTitleParentChanged: function (event) {
    // event.target.value可以获取到input中的值,这点我之前给input加上ref,报错提示我只有父类的组件才能使用ref
    // 这大概是jquery的方法获取到的dom吧?
    // 在网上找到这段话:通常会有一个事件对象传入到React的事件处理函数中,
    // 类似于原生的JavaScript事件监听器的写法。这里的handleComplete方法会接受一个事件对象,并通过存取event.target.value值为textarea赋值,
    // 在事件处理器中,使用event.target.value获取表单中input值是一个常规的方法,尤其在onClange事件处理器中。
    var keyword = event.target.value; 
    var scores = [];
    this.state.dataSourceBase.map(function (obj) {
      if (obj.title.indexOf(keyword) >= 0) {
        scores.push(obj);
      }
    });
    this.setState({
      dataSource: scores
    });
  }

然后,你会看到一个这样的效果图:

上面也引申出一个问题,两个独立的组件,将如何传递数据和事件呢?这将是以后的章节中需要探讨的。

参考资料:

你可以阅读这篇文章进一步学习 React组件之间传值

本节源码地址(component communication)

退出移动版