18、React之分页组件,含勾选、过滤、任意表头纵横合并、子组件向父组件传值、redux、redux-thunk、react-router、react-redux

一、react之表格和分页组件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>react之表格和分页组件之React16.4.0版</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<style>
  table{
    border-collapse: collapse;
    border: 1px solid #cbcbcb;
    width:1000px;
    background:#ffffff;
    text-align:center;
  }
  table td,table th {
    padding: 5px;
    border: 1px solid #cbcbcb;
    font-weight:100
  }
  div button{
    color:gray;
    margin-right:5px
  }
  .div{
    color:red;
    width:1000px;
    padding:10px 0;
  }
</style>
</head>
<body>
  <div class="div">
    <div>表格组件的功能:</div>
    <div>(1)带过滤条件、</div>
    <div>(2)表头可以合并、</div>
    <div>(3)排序(暂不实现)、</div>
    <div>(4)表体可以嵌套表格(暂不实现)、</div>
    <div>(5)3种勾选(选择一项、选择一页、选择所有页)、</div>
    <div>(6)翻页记忆、</div>
    <div>(7)分页。</div>
  </div>
  <div ></div>
</body>
<script type="text/babel">
const container = document.getElementById('container');
function TwoImg(props) {
  var checkImg = {
    yes: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
    no: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII=',
  };
  return (<img src={props.isTrue?checkImg.yes:checkImg.no} onClick={props.clickImg.bind(this)}/>)
}
//一、以下TablePage是表格组件,约280行
class TablePage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.initThead = [];
    this.dataIndexs = [];//表头索引,表头和表体关联的依据
    this.maxRowspan = 1;//表头最大跨行数
    if(props.giveParentInstance){
      props.giveParentInstance(this)//子组件向父组件传值-之三-给函数传参并执行
    } 
  }
  //表头跨行跨栏的总思路
  //(1)处理跨行的时候,上面决定下面,要看上面有几个单行
  //(2)处理跨栏的时候,下面决定上面,要看下面有几个单栏
  //一、以下2个函数解决“表头跨行”问题
  //1、获取表头“最大跨行数”
  getMaxRowspanAndDataIndexs(columns,thisRowspan){
    var that=this;
    columns.forEach(function(item,index) {
      if(item.children){
        thisRowspan+=1;
        if(thisRowspan>that.maxRowspan) that.maxRowspan = thisRowspan;
        that.getMaxRowspanAndDataIndexs(item.children,thisRowspan)
      }else{
        that.dataIndexs.push(item.dataIndex)//生成表头“数据索引”
      }
    });
  }
  //2、添加表头“每栏跨行数”
  addEachColumnRowspan(columns,thisRowspan){
    var that=this;
    columns.forEach(function(item,index) {
      if(item.children){
        item.thisRowspan=1;
        that.addEachColumnRowspan(item.children,thisRowspan-1)
      }else{
        item.thisRowspan=thisRowspan;
      }
    });
  }
  //二、以下2个函数解决“表头跨栏”问题
  //3、添加表头“每栏跨栏数”
  addEachColumnColspan(columns){
    var that=this;
    columns.forEach(function(item) {
      if(item.thisColspan)return;
      if(item.children){
        that.addThisColumnColspan(item,item.children)
      }else{
        item.thisColspan=1;
      }
    });
  }
  //4、当某栏有多个子栏时,添加“该栏及其子栏的跨栏数”
  addThisColumnColspan(item,children){
    var that=this;
    children.forEach(function(child) {
      var thisChildren= child.children;
      if(thisChildren){//item的子级还有子级
        //下面添加item栏的跨栏数
        that.addThisColumnColspan(item,thisChildren);
        //下面添加item栏子栏的跨栏数
        that.addEachColumnColspan(children);
      }else{
        if(item.thisColspan){
          item.thisColspan+=1;
        }else{
          item.thisColspan=1;
        }
        child.thisColspan=1;
      }
    });
  }
  getInitThead(){
    for(var i=0;i<this.maxRowspan;i++){
      this.initThead.push([]);
    }
  } 
  getCenterThead(columns,initThead,index){
    var that=this;
    columns.forEach(function(item,indexIn){
      var itemTitle;
      if(item.title){
        itemTitle=item.title;
      }else{
        itemTitle=<TwoImg isTrue={that.props.checkSource.isSelectNowPage} clickImg={that.clickThisPage.bind(that,that.props.dataSource)}/>      
      }
      initThead[index].push(<th key={indexIn+Math.random()} rowSpan={item.thisRowspan} colSpan={item.thisColspan} dataindex={item.dataIndex||''}>{itemTitle}</th>)
      var children=item.children;
      if(children){
        that.getCenterThead(children,initThead,index+1)
      }
    })
  }  
  getLastThead(thead,initThead){
    var that=this;
    initThead.forEach(function(item,index){
      thead.push(<tr key={index}>{item}</tr>)
    })
  }
  getTbody(dataSource,trBody){
    var that=this;
    dataSource.forEach(function(tr,index){
      var trSingle=[];
      for(var i=0;i<that.dataIndexs.length;i++){
        var indexIn=that.dataIndexs[i];
        var td;
        if(indexIn === 'checkQC'){
          td = <TwoImg isTrue={tr.state} clickImg={that.clickSingleItem.bind(that,tr,dataSource)}/> 
        }else{
          td = tr[indexIn];
        }
        trSingle.push(<td key={indexIn}>{td}</td>) 
      }
      trBody.push(<tr key={index}>{trSingle}</tr>); 
    });
  }
  componentWillUpdate(nextProps) {
    this.signCheckbox(nextProps) 
  }
  setAllState(){
    this.props.checkboxClick({
      allIncludedIds: this.props.checkSource.allIncludedIds,
      allExcludedIds: this.props.checkSource.allExcludedIds,
      isSelectNowPage: this.props.checkSource.isSelectNowPage,
      isSelectAllPages: this.props.checkSource.isSelectAllPages,
      textAllPages: this.props.checkSource.textAllPages,
    })
  }
  clickChildAllPages(itemArray) {//所有页所有条目复选框被点击时执行的函数
    if(this.props.checkSource.isSelectAllPages){
      if(this.props.checkSource.allExcludedIds.length>0){
        this.props.checkSource.isSelectAllPages = true;
        this.props.checkSource.isSelectNowPage = true;
        this.props.checkSource.textAllPages= '已启用,无排除项!';
        itemArray.forEach(function (item) {
          item.state = true;
        });
      }else if(this.props.checkSource.allExcludedIds.length==0){
        this.props.checkSource.isSelectAllPages = false;
        this.props.checkSource.isSelectNowPage = false;
        this.props.checkSource.textAllPages= '未启用,无选择项!';
        itemArray.forEach(function (item) {
          item.state = false;
        });
      }
    }else{
      this.props.checkSource.isSelectAllPages = true;
      this.props.checkSource.isSelectNowPage = true;
      this.props.checkSource.textAllPages= '已启用,无排除项!';
      itemArray.forEach(function (item) {
        item.state = true;
      });
    }
    this.props.checkSource.allExcludedIds = [];
    this.props.checkSource.allIncludedIds = [];
    this.setAllState()
  }
  clickThisPage(itemArray) {//当前页所有条目复选框被点击时执行的函数
  //onClick={this.clickThisPage.bind(this,params.tableDatas)
    var that = this;
    this.props.checkSource.isSelectNowPage = !this.props.checkSource.isSelectNowPage;
    itemArray.forEach(function (item) {
      item.state = that.props.checkSource.isSelectNowPage;
      if (item.state) {
        that.delID(item[that.props.idKey], that.props.checkSource.allExcludedIds);
        that.addID(item[that.props.idKey], that.props.checkSource.allIncludedIds);
      } else {
        that.delID(item[that.props.idKey], that.props.checkSource.allIncludedIds);
        that.addID(item[that.props.idKey], that.props.checkSource.allExcludedIds);
      }
    });
    if(this.props.checkSource.isSelectAllPages){
      if(this.props.checkSource.isSelectNowPage && this.props.checkSource.allExcludedIds.length === 0){
        this.props.checkSource.textAllPages = '已启用,无排除项!';
      }else{
        this.props.checkSource.textAllPages = '已启用,已排除'+ this.props.checkSource.allExcludedIds.length + '项!排除项的ID为:' + this.props.checkSource.allExcludedIds;
      }
    }else{
      if(!this.props.checkSource.isSelectNowPage && this.props.checkSource.allIncludedIds.length === 0){
        this.props.checkSource.textAllPages='未启用,无选择项!';
      }else{
        this.props.checkSource.textAllPages = '未启用,已选择' + this.props.checkSource.allIncludedIds.length + '项!选择项的ID为:' + this.props.checkSource.allIncludedIds;
      }
    }
    this.setAllState()
  }
  clickSingleItem(item, itemArray) {//当前页单个条目复选框被点击时执行的函数
    var that = this;
    item.state = !item.state;
    if (item.state) {
      this.props.checkSource.isSelectNowPage = true;
      this.addID(item[this.props.idKey], this.props.checkSource.allIncludedIds);
      this.delID(item[this.props.idKey], this.props.checkSource.allExcludedIds);
      itemArray.forEach(function (item) {
        if (!item.state) {
          that.props.checkSource.isSelectNowPage = false;
        }
      });
    } else {
      this.props.checkSource.isSelectNowPage = false;
      this.addID(item[this.props.idKey], this.props.checkSource.allExcludedIds);
      this.delID(item[this.props.idKey], this.props.checkSource.allIncludedIds);
    }
    if(this.props.checkSource.isSelectAllPages){
      if(this.props.checkSource.isSelectNowPage && this.props.checkSource.allExcludedIds.length === 0){
        this.props.checkSource.textAllPages = '已启用,无排除项!';
      }else{
        this.props.checkSource.textAllPages = '已启用,已排除'+ this.props.checkSource.allExcludedIds.length + '项!排除项的ID为:' + this.props.checkSource.allExcludedIds;
      }
    }else{
      if(!this.props.checkSource.isSelectNowPage && this.props.checkSource.allIncludedIds.length === 0){
        this.props.checkSource.textAllPages='未启用,无选择项!';
      }else{
        this.props.checkSource.textAllPages = '未启用,已选择' + this.props.checkSource.allIncludedIds.length + '项!选择项的ID为:' + this.props.checkSource.allIncludedIds;
      }
    }
    this.setAllState()
  }
  signCheckbox(nextProps) {//标注当前页被选中的条目,在翻页成功后执行。
    var that = this;
    if(nextProps.checkSource.isSelectAllPages){
      nextProps.checkSource.isSelectNowPage = true;
      nextProps.dataSource.forEach(function (item) {
        var thisID = item[nextProps.idKey];
        var index = nextProps.checkSource.allExcludedIds.indexOf(thisID);
        if (index > -1) {
          item.state = false;
          nextProps.checkSource.isSelectNowPage = false;
        } else {
          item.state = true;
        }
      });
    }else{
      nextProps.checkSource.isSelectNowPage = true;
      nextProps.dataSource.forEach(function (item) {
        var thisID = item[nextProps.idKey];
        var index = nextProps.checkSource.allIncludedIds.indexOf(thisID);
        if (index === -1) {
          item.state = false;
          nextProps.checkSource.isSelectNowPage = false;
        } else {
          item.state = true;
        }
      });
    }
    this.state.isSelectNowPage=nextProps.checkSource.isSelectNowPage;
  }
  addID(id, idArray) {
    var index = idArray.indexOf(id);
    if (index === -1) {
      idArray.push(id);//如果当前页的单项既有勾选又有非勾选,这时勾选当前页,需要这个判断,以免重复添加
    }
  }
  delID(id, idArray) {
    var index = idArray.indexOf(id);
    if (index > -1) {
      idArray.splice(index, 1)
    }
  }
  render() {
    var that=this;
    var thead=[];
    var tbody=[];
    var trBody=[];
    var columns=this.props.columns;
    var dataSource=this.props.dataSource;
    this.initThead = [];
    this.dataIndexs = [];
    this.getMaxRowspanAndDataIndexs(columns,1);
    this.addEachColumnRowspan(columns,this.maxRowspan);
    this.addEachColumnColspan(columns);
    this.getInitThead();
    this.getCenterThead(columns,this.initThead,0);
    this.getLastThead(thead,this.initThead);
    this.getTbody(dataSource,trBody);
    return (
      <div>
        <table>
          <thead>
           {thead} 
          </thead>
          <tbody>
           {trBody} 
          </tbody>
        </table> 
      </div>
    )
  }
}
//二、以下DevidePage是分页组件,约150行
class DevidePage extends React.Component {
  constructor(props) {
    super(props);
    this.state = { };
  }
  componentDidMount(){
    document.getElementById("inputQC").addEventListener("keydown", this.onKeyDown.bind(this))
  }
  componentDidUpdate(){
    document.getElementById("inputQC").addEventListener("keydown", this.onKeyDown.bind(this))
  }
  componentWillUnmount(){
    document.getElementById("inputQC").removeEventListener("keydown", this.onKeyDown.bind(this))
  }
  inputChange() {
    var value = parseInt(this.refs.input.value);
    var allPagesNum = this.props.divideSource.allPagesNum;
    if(value < allPagesNum && value > 1) {
      this.props.divideSource.inputValue = value
    }else if(value >= allPagesNum) {
      this.props.divideSource.inputValue = allPagesNum
    }else{//包含 value <= 1和value=其它非数字字符
      this.props.divideSource.inputValue = 1
    }
  }
  clickButton(value){
    var nowPageNum = null;
    if(value === 'front'){
      this.props.divideSource.nowPageNum--;
      nowPageNum = this.props.divideSource.nowPageNum
    }else if(value === 'back'){
      this.props.divideSource.nowPageNum++;
      nowPageNum = this.props.divideSource.nowPageNum
    }else if(value === 'leap'){
      this.inputChange();
      nowPageNum = this.props.divideSource.inputValue
    }else{
      nowPageNum = value
    }
    this.refs.input.value = nowPageNum;
    this.props.divideClick(nowPageNum,this.props.divideSource.eachPageItemsNum);
  }
  onKeyDown(event){
    if(event.key === 'Enter'){
      this.inputChange();
      this.refs.input.value = this.props.divideSource.inputValue;
      this.props.divideClick(this.props.divideSource.inputValue,this.props.divideSource.eachPageItemsNum);
    }
  }
  pageNumLeap(){
    var eachPageItemsNum = this.refs.select.value;
    this.props.divideSource.eachPageItemsNum = eachPageItemsNum;
    this.props.divideClick(1,eachPageItemsNum);
  }
  render() {
    var numButton=[];
    var divideSource = this.props.divideSource;
    //1、以下处理与分页相关的数字
    var nowPageNum = divideSource.nowPageNum;
    var allPagesNum = divideSource.allPagesNum;
    var inputValue = divideSource.inputValue;
    var eachPageItemsNum = divideSource.eachPageItemsNum;
    var allItemsNum = divideSource.allItemsNum;
    //2、以下是分页组件本身
    if (allPagesNum >= 1 && allPagesNum <= 10) {
      for (var i = 1; i <= allPagesNum; i++) {
        numButton.push(<button key={i} red'}:{color:'gray'}} onClick={this.clickButton.bind(this,i)}>{i}</button>)
      }
    } else if (allPagesNum >= 11) {
      if (nowPageNum > 8) {
        numButton.push(<button key={1} onClick={this.clickButton.bind(this,1)}>{1}</button>);
        numButton.push(<button key={2} onClick={this.clickButton.bind(this,2)}>{2}</button>);
        numButton.push(<button key={3} onClick={this.clickButton.bind(this,3)}>{3}</button>);
        numButton.push(<button key={'front'} disabled>{'...'}</button>);
        numButton.push(<button key={nowPageNum-2} onClick={this.clickButton.bind(this,nowPageNum-2)}>{nowPageNum-2}</button>);
        numButton.push(<button key={nowPageNum-1} onClick={this.clickButton.bind(this,nowPageNum-1)}>{nowPageNum-1}</button>);
        numButton.push(<button key={nowPageNum} red'}} onClick={this.clickButton.bind(this,nowPageNum)}>{nowPageNum}</button>);
      } else {
        for (i = 1; i <= nowPageNum; i++) {
          numButton.push(<button key={i} red'}:{color:'gray'}} onClick={this.clickButton.bind(this,i)}>{i}</button>)
        }
      }
      // 以上当前页的左边,以下当前页的右边
      if (allPagesNum - nowPageNum >= 7) {
        numButton.push(<button key={nowPageNum+1} onClick={this.clickButton.bind(this,nowPageNum+1)}>{nowPageNum+1}</button>);
        numButton.push(<button key={nowPageNum+2} onClick={this.clickButton.bind(this,nowPageNum+2)}>{nowPageNum+2}</button>);
        numButton.push(<button key={'back'} disabled>{'...'}</button>);
        numButton.push(<button key={allPagesNum-2} onClick={this.clickButton.bind(this,allPagesNum-2)}>{allPagesNum-2}</button>);
        numButton.push(<button key={allPagesNum-1} onClick={this.clickButton.bind(this,allPagesNum-1)}>{allPagesNum-1}</button>);
        numButton.push(<button key={allPagesNum} onClick={this.clickButton.bind(this,allPagesNum)}>{allPagesNum}</button>);
      } else {
        for (var i = nowPageNum + 1; i <= allPagesNum; i++) {
          numButton.push(<button key={i} onClick={this.clickButton.bind(this,i)}>{i}</button>)
        }
      }
    }
    //3、以下处理每页显示条数
    var selectOption=[];
    var numOptions=this.props.numOptions;
    if(!numOptions){numOptions=[10,20,30,40,50]}; 
    for(var i=0;i<numOptions.length;i++){
      selectOption.push(<option value={numOptions[i]} key={i} >{numOptions[i]}</option>)
    }
    //4、以下处理最右侧说明文字
    var textTemp=this.props.text||{};
    var text = {
      unit: textTemp.unit || "条,",
      frontMoreText: textTemp.frontMoreText || "",
      totalText: textTemp.totalText || "共",
      totalUnit: textTemp.totalUnit || "项",
      backMoreText: textTemp.backMoreText || "",
    };
    //5、以下渲染分页组件 
    return (
      <div block',display:"flex",width:"1000px",marginTop:"20px"}}>
        <div flex"}}>
          <button 5px"}} disabled={nowPageNum===1} onClick={this.clickButton.bind(this,'front')}>上一页</button>
          <div>{ numButton }</div>
          <button disabled={nowPageNum===allPagesNum} onClick={this.clickButton.bind(this,'back')}>下一页</button>
        </div>
        <div flex", flex:1, justifyContent:"flex-end"}}>
          <div 15px"}}>
            <span>转到第</span>
            <input  key={nowPageNum==1?Math.random():'key'} type="text" 30px",margin:"0 5px"}} ref="input" onChange={this.inputChange.bind(this)} onKeyDown={this.onKeyDown.bind(this,event)} defaultValue={inputValue}/>
            <span>页</span>
            <button 0 5px"}} onClick={this.clickButton.bind(this,'leap')}>Go</button>
          </div>
          <div>
            <span>每页显示</span>
            <select 0 5px"}} ref="select" defaultValue={eachPageItemsNum||10} onChange={this.pageNumLeap.bind(this)}>
              { selectOption }
            </select>
            <span>{text.unit}</span>
          </div>
          <div>
            <span>{text.frontMoreText}</span>
            <span>{text.totalText}</span>
            <span>{allItemsNum||0}</span>
            <span>{text.totalUnit}</span>
            <span>{text.backMoreText}</span>
          </div>
        </div>   
      </div>
    )
  }
}
//三、以下WholePage是页面组件,根据自己的独特需求写
class WholePage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filter: {
        input:'',
        select:1
      },
      dataSource: [],
      divideSource:{
        nowPageNum: 1,
        allPagesNum: 1,
        allItemsNum: 1,
        eachPageItemsNum: 10,
        inputValue:1,
      },
      checkSource:{
        allIncludedIds: [],
        allExcludedIds: [],
        isSelectAllPages: false,
        isSelectNowPage: false,
        textAllPages: '未启用,无选择项!', 
      },
    };
    this.columns = [
      {
        title: '',
        dataIndex: 'checkQC',
      },
      {
        title: '序号',
        dataIndex: 'order',
      },
      {
        title: '个人信息',
        children: [
          {
            title: '姓名',
            dataIndex: 'name'
          },
          {
            title: '性别',
            dataIndex: 'sex'
          },
          {
            title: '年龄',
            dataIndex: 'age'
          },
        ],
      },
      {
        title: '学校以下(学习)',
           children: [
             {
               title: '学校',
               dataIndex: 'school'
             },
             {
               title: '年级以下',
               children: [
                 {
                   title: '年级',
                   dataIndex: 'grade'
                 },
                 {
                   title: '班级以下',
                   children: [
                    {
                      title: '班级',
                      dataIndex: 'class'
                    },
                    {
                      title: '科目以下',
                      children: [
                        {
                          title: '科目1',
                          dataIndex: 'cross1'
                        },
                        {
                          title: '科目2',
                          dataIndex: 'cross2'
                        },
                      ],
                    },
                   ],
                 },
               ],
             },
           ],
      },
      {
        title: '省级以下(地址)',
           children: [
             {
               title: '省级',
               dataIndex: 'province'
             },
             {
               title: '市级以下',
               children: [
                 {
                   title: '市级',
                   dataIndex: 'city'
                 },
                 {
                   title: '县级以下',
                   children: [
                    {
                      title: '县级',
                      dataIndex: 'coutry'
                    },
                    {
                      title: '乡级以下',
                      children: [
                        {
                          title: '乡级',
                          dataIndex: 'town'
                        },
                        {
                          title: '村级以下',
                          children: [
                            {
                              title: '村级',
                              dataIndex: 'village'
                            },
                            {
                              title: '组级',
                              dataIndex: 'team'
                            },
                          ],
                        },
                      ],
                    },
                   ],
                 },
               ],
             },
           ],
      }
    ];  
  };
  componentWillMount() { 
    this.divideClick(1,this.state.divideSource.eachPageItemsNum)
  }
  divideClick(nowPageNum,eachPageItemsNum) { 
    var data=[]; 
    var allItemsNum = 193;
    var nowPageNum = nowPageNum||1; 
    var eachPageItemsNum = eachPageItemsNum||10; 
    var allPagesNum = Math.ceil(allItemsNum/eachPageItemsNum);
    for(var i=0;i<allItemsNum;i++){
      var obj={
        id:'id'+(i+1),
        order:i+1,
        name:'姓名'+(i+1),
        sex:['男','女'][i%2],
        age:(i+11)+'岁',
        province:'第'+(i+11)+'省',
        city:(i+11)+'市',
        coutry:(i+11)+'县',
        town:(i+11)+'乡',
        village:(i+11)+'村',
        team:(i+11)+'组',
        school:(i+11)+'学校',
        grade:(i+11)+'年级',
        class:(i+11)+'班',
        cross1:(i+11)+'科',
        cross2:(i+11)+'科',
      };
      data.push(obj)
    };
    var dataSource = data.slice((nowPageNum-1)*eachPageItemsNum,nowPageNum*eachPageItemsNum);
    this.setState({
      dataSource: dataSource,
      divideSource: {
        nowPageNum: nowPageNum,
        allPagesNum: allPagesNum,
        allItemsNum: allItemsNum,
        eachPageItemsNum: eachPageItemsNum,
        inputValue: nowPageNum,
      },
    })
  }
  checkboxClick(object) { 
    this.setState({
      allIncludedIds: object.allIncludedIds,
      allExcludedIds: object.allExcludedIds,
      isSelectAllPages: object.isSelectAllPages,
      isSelectNowPage: object.isSelectNowPage,
      textAllPages: object.textAllPages,
    })
  }
  getChildInstance(that){//子组件向父组件传值-之一-在父组件定义函数
    this.childRef=that;//把子组件的实例赋值给父组件的childRef属性。非常重要!!!
  }
  clickParentAllPages(dataSource){
    this.childRef.clickChildAllPages(dataSource)//在父组件里调用子组件的方法。非常重要!!!
  }
  changeValue(key,event){
    this.setState({
      filter:{...this.state.filter,[key]:event.target.value}
    })
  }
  render() {
    var {dataSource,divideSource,checkSource,filter}={...this.state}; 
    return (
      <div> 
        <div>以下是过滤示例</div>
        <div border':'1px solid #cbcbcb','width':'980px','padding':'10px','margin':'6px 0'}}>
          <div display':'flex'}}>
            <div width':'212px'}}> 
              <label paddingRight':'4px'}}>输入框示例</label>
              <input type='text' placeholder='请输入' onChange={this.changeValue.bind(this,'input')}  border':'1px solid #cbcbcb','width':'100px'}}/>
            </div>
            <div width':'174px'}}> 
              <label paddingRight':'4px'}}>下拉框示例</label>
              <select onChange={this.changeValue.bind(this,'select')}>
                <option value={1}>1分钟</option>
                <option value={5}>5分钟</option>
                <option value={10}>10分钟</option>
                <option value={30}>30分钟</option>
                <option value={60}>60分钟</option>
              </select>
            </div>
            <div width':'500px'}}> 
              <span>过滤条件为:{JSON.stringify(this.state.filter)}</span> 
              <span padding':'0 10px'}}></span>  
              <button onClick={this.divideClick.bind(this,3,10)}>过滤</button>
              <button onClick={this.divideClick.bind(this,1,10)}>刷新</button>
            </div>
          </div>
        </div>
        <div paddingBottom':'4px'}}>
          <span paddingRight':'10px'}}><TwoImg isTrue={checkSource.isSelectAllPages && checkSource.allExcludedIds.length===0} clickImg={this.clickParentAllPages.bind(this,dataSource)}/></span>     
          <span>{checkSource.textAllPages}</span>
        </div>
        <TablePage 
          idKey='id' 
          columns={this.columns}
          dataSource={dataSource}
          checkSource={checkSource}
          checkboxClick={this.checkboxClick.bind(this)}
          giveParentInstance={this.getChildInstance.bind(this)}//子组件向父组件传值-之二-将函数传给子组件
          />
        <DevidePage 
          divideSource={divideSource} 
          divideClick={this.divideClick.bind(this)}
          />  
      </div>
    )
  }
}
ReactDOM.render(<WholePage/>, container);
</script>
</html>
 
二、redux实际运用
1、参数的定义
function functionA(createStore3) {//7、接收functionB的返回值createStore3
  return function createStore4(reducer0, preloadedState0, enhancer0) {//8、返回值为createStore4
    //9、实际执行createStore4(reducer, preloadedState),此处加工参数reducer, preloadedState,传给下面的createStore3
    var store3=createStore3(reducer1, preloadedState1, enhancer1);
    //16、此处对createStore3的返回值store3进行加工,下面return的是createStore4的返回值,也是最终的返回值
    return {
      dispatch: dispatch3,
      subscribe: subscribe3,
      getState: getState3,
      replaceReducer: replaceReducer3
    }
  }
};
function functionB(createStore2) {//5、接收functionC的返回值createStore2
  return function createStore3(reducer1, preloadedState1, enhancer1) {//6、返回值为createStore3
    //10、此处加工参数,传给下面的createStore2
    var store2=createStore2(reducer2, preloadedState2, enhancer2);
    //15、此处对createStore2的返回值store2进行加工,下面return的是createStore3的返回值
    return {
      dispatch: dispatch2,
      subscribe: subscribe2,
      getState: getState2,
      replaceReducer: replaceReducer2
    }
  }
};
function functionC(createStore1) {//3、接收functionD的返回值createStore1
  return function createStore2(reducer2, preloadedState2, enhancer2) {//4、返回值为createStore2
    //11、此处加工参数,传给下面的createStore1
    var store1=createStore1(reducer3, preloadedState3, enhancer3);
    //14、此处对createStore1的返回值store1进行加工,下面return的是createStore2的返回值
    return {
      dispatch: dispatch1,
      subscribe: subscribe1,
      getState: getState1,
      replaceReducer: replaceReducer1
    }
  }
};
function functionD(createStore0) {//1、实际执行functionD(createStore)
  return function createStore1(reducer3, preloadedState3, enhancer3) {//2、返回值为createStore1
  //12、此处加工参数,传给下面的createStore0
    var store0=createStore0(reducer4, preloadedState4, enhancer4);
    //13、此处对createStore0的返回值store0进行加工,下面return的是createStore1的返回值
    return {
      dispatch: dispatch0,
      subscribe: subscribe0,
      getState: getState0,
      replaceReducer: replaceReducer0
    }
  }
};
2、createStore函数的定义与执行
(1)定义
function createStore(reducer, preloadedState, enhancer) {
  return enhancer(createStore)(reducer, preloadedState);
}
(2)执行
createStore(
  rootReducer,
  preloadedState,
  compose(arrayFunction)
)
3、compose的定义与执行
(1)定义
var arrayFunction = [functionA, functionB, functionC, functionD];
function compose(arrayFunction) {
  return arrayFunction.reduce(function (total, next) {//reduce用作高阶函数,compose其它函数
    // reduce第1次执行时,total是functionA,next是functionB,执行结果为functionOne
    // function functionOne() {
    //   return functionA(functionB.apply(undefined, arguments));
    // }
    // reduce第2次执行时,total是functionOne,next是functionC,执行结果为functionTwo
    // function functionTwo() {
    //   return functionOne(functionC.apply(undefined, arguments));
    // }
    // reduce第3次执行时,total是functionTwo,next是functionD,执行结果为functionThree
    // function functionThree() {
    //   return functionTwo(functionD.apply(undefined, arguments));
    // }
    // reduce将最后一次执行结果functionThree暴露出去
    return function () {
      return total(next.apply(undefined, arguments));
    };
  })
}
(2)compose(arrayFunction)执行,返回functionThree
4、enhancer(createStore)(reducer, preloadedState)执行,就是functionThree(createStore)(reducer, preloadedState)执行
(1)enhancer(createStore)执行,就是functionThree(createStore)执行,最终返回createStore4
//第1次执行时,functionThree(createStore0),functionD(createStore0),返回createStore1
//第2次执行时,functionTwo(createStore1),functionC(createStore1),返回createStore2
//第3次执行时,functionOne(createStore2),functionB(createStore2),返回createStore3
//第4次执行时,functionA(createStore3),返回createStore4
(2)createStore4(reducer, preloadedState)执行
//1、给createStore4传参并执行,进而给createStore3、createStore2、createStore1、createStore0传参并执行
//2、给createStore0的返回值加工,进而给createStore1、createStore2、createStore3、createStore4的返回值加工,生成最终store

5、createStore实际运用(真实案例)
import { createStore, applyMiddleware, compose } from 'redux';
import reduxThunk from 'redux-thunk';//从UI组件直接dispatch action。它的主要思想是扩展action,使得action从只能是一个对象变成还可以是一个函数。
import rootReducer from 'reducers/index';
import DevTools from 'containers/DevTools';
export default function configureStore(preloadedState) {
  const store = createStore(
    rootReducer,
    preloadedState,
    compose(
      applyMiddleware(reduxThunk),
      DevTools.instrument()
    )
  )
  return store
}
6、相关源码解析
(1)createStore
function createStore(reducer, preloadedState, enhancer) {
  var _ref2;
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState;
    preloadedState = undefined;
  }
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.');
    }
    return enhancer(createStore)(reducer, preloadedState);  }
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.');
  }
  var currentReducer = reducer;
  var currentState = preloadedState;
  var currentListeners = [];
  var nextListeners = currentListeners;
  var isDispatching = false;
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice();
    }
  }
  function getState() {
    return currentState;
  }
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.');
    }
    var isSubscribed = true;
    ensureCanMutateNextListeners();
    nextListeners.push(listener);
    return function unsubscribe() {
      if (!isSubscribed) {
        return;
      }
      isSubscribed = false;
      ensureCanMutateNextListeners();
      var index = nextListeners.indexOf(listener);
      nextListeners.splice(index, 1);
    };
  }
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
    }
    if (typeof action.type === 'undefined') {
      throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
    }
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.');
    }
    try {
      isDispatching = true;
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }
    var listeners = currentListeners = nextListeners;
    for (var i = 0; i < listeners.length; i++) {
      var listener = listeners[i];
      listener();
    }
    return action;
  }
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.');
    }
    currentReducer = nextReducer;
    dispatch({ type: ActionTypes.INIT });
  }
  function observable() {
    var _ref;
    var outerSubscribe = subscribe;
    return _ref = {
      subscribe: function subscribe(observer) {
        if (typeof observer !== 'object') {
          throw new TypeError('Expected the observer to be an object.');
        }
        function observeState() {
          if (observer.next) {
            observer.next(getState());
          }
        }
        observeState();
        var unsubscribe = outerSubscribe(observeState);
        return { unsubscribe: unsubscribe };
      }
    }, _ref[result] = function () {
      return this;
    }, _ref;
  }
  dispatch({ type: ActionTypes.INIT });
  return _ref2 = {
    dispatch: dispatch,
    subscribe: subscribe,
    getState: getState,
    replaceReducer: replaceReducer
  }, _ref2[result] = observable, _ref2;
}
(2)compose
function compose() {
  for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
    funcs[_key] = arguments[_key];
  }
  if (funcs.length === 0) {
    return function (arg) {
      return arg;
    };
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce(function (a, b) {//reduce将最后一次计算结果暴露出去
    return function () {
      return a(b.apply(undefined, arguments));
    };
  });
} 
(3)applyMiddleware
function applyMiddleware() {//这是一个加工dispatch的中间件
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }
  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];
      var middlewareAPI = {
        getState: store.getState,
        dispatch: function dispatch(action) {//此处为什么不是dispatch:store.dispatch
          return _dispatch(action);
        }
      };
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);//return reduxThunk(_ref)
      });
      _dispatch = compose.apply(undefined, chain)(store.dispatch);//_dispatch = (function (next){})(store.dispatch) ,这是dispatch的新定义。
      return _extends({}, store, {//store里的dispatch,被这里的dispatch覆盖
        dispatch: _dispatch
      });
    };
  };
}
(4)combineReducers
function combineReducers(reducers) {
  //下面是关于reducers的定义,它的key如fn1、fn2、fn3、fn4后来也成了state的key。此论依据“非常重要!”前面的代码
  // finalReducers = reducers = {
  //   fn1 : function(){ return { key1 : "value1" } },
  //   fn2 : function(){ return { key2 : "value2" } },
  //   fn3 : function(){ return { key3 : "value3" } },
  //   fn4 : function(){ return { key4 : "value4" } },
  // }
  var reducerKeys = Object.keys(reducers);
  var finalReducers = {};
  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i];
    {
      if (typeof reducers[key] === 'undefined') {
        warning('No reducer provided for key "' + key + '"');
      }
    }
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }
  var finalReducerKeys = Object.keys(finalReducers);
  var unexpectedKeyCache = void 0;
  {
    unexpectedKeyCache = {};
  }
  var shapeAssertionError = void 0;
  try {
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }
  return function combination() {//这个返回值就是createStore(reducer, preloadedState, enhancer)中的reducer
    // 下面是关于state的定义,它的key如fn1、fn2、fn3、fn4来自于reducers的key。此论依据“非常重要!”前面的代码
    // nextState = state = { } = {
    //   fn1 : { key1 : "value1" },
    //   fn2 : { key2 : "value2" },
    //   fn3 : { key3 : "value3" },
    //   fn4 : { key4 : "value4" }
    // }
    var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var action = arguments[1];
    if (shapeAssertionError) {
      throw shapeAssertionError;
    }
    {
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
      if (warningMessage) {
        warning(warningMessage);
      }
    }
    var hasChanged = false;
    var nextState = {};
    for (var _i = 0; _i < finalReducerKeys.length; _i++) {
      var _key = finalReducerKeys[_i];
      var reducer = finalReducers[_key];
      var previousStateForKey = state[_key];//非常重要!previousStateForKey可能为undefined
      var nextStateForKey = reducer(previousStateForKey, action);//非常重要!可能执行reducer(undefined,action),即执行reducer(defaultState,action)。
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(_key, action);
        throw new Error(errorMessage);
      }
      nextState[_key] = nextStateForKey;
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;           
      // const defaultState = {
      //   loading: true,
      // };
      // export default function accountReducer(state = defaultState, action) {
      //   switch (action.type) {
      //     case "isRegister":
      //       //redux.js纯函数执行后,需要给state重新赋值,否则hasChanged不会为true,vuex.js不需要这样,因为它采用了递归监听
      //       return {...state, loading: action.loading||false }//action匹配成功,state的引用就改变了
      //     default:
      //       return state;//action匹配不成功,state的引用就不改变了
      //   }
      // }
    }
    //遍历结束,一次性返回结果。
    return hasChanged ? nextState : state;
  };
}
(5)其它
function componentWillMount() {
  const current = [
    { location: "222", url: "/" },
    { location: "333", url: "/carDetail" },
  ];
  this.props.dispatch(menuChange(current))
}
export const menuChange = function (key) {
  return function (dispatch) {
    dispatch({
      type: "menu_change",
      key
    })
  }
}

三、redux-thunk实际运用
1、reduxThunk核心代码
//从UI组件直接dispatch action。它的主要思想是扩展action,使得action从只能是一个对象变成还可以是一个函数。
function reduxThunk(_ref) {//middleware(middlewareAPI)
  var dispatch = _ref.dispatch;
  var getState = _ref.getState;
  return function (next) {//即return function(store.dispatch);next是dispatch的旧定义,即applyMiddleware函数的chain数组的下一项的执行结果。 
    return function (action) {//返回的函数是dispatch的新定义;其中action是参数,原本只能是对象,经改造后还可以是函数。
      if (typeof action === 'function') {
        return action(dispatch, getState);//把旧的dispatch、getState传进自定义函数参数里
      }
      return next(action);
    };
  };
}
2、reduxThunk全部代码
(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();//common.js模块下执行,factory()的执行结果为null
    else if (typeof define === 'function' && define.amd)
        define([], factory);//require.js异步模块下执行
    else if (typeof exports === 'object')
        exports["ReduxThunk"] = factory();
    else
        root["ReduxThunk"] = factory();
})(
  this, 
  function () {
    return (function (modules) {
      var installedModules = {};
      function __webpack_require__(moduleId) {
        if (installedModules[moduleId])
          return installedModules[moduleId].exports;
        var module = installedModules[moduleId] = {
          exports: {},
          id: moduleId,
          loaded: false        
        };
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
        module.loaded = true;
        return module.exports;   
      }
      __webpack_require__.m = modules;
      __webpack_require__.c = installedModules;
      __webpack_require__.p = "";
      return __webpack_require__(0); 
    })([function (module, exports, __webpack_require__) {
        module.exports = __webpack_require__(1);
      },function (module, exports) {
            'use strict';
            exports.__esModule = true;
            exports['default'] = reduxThunk;//这是最终的注入
            function reduxThunk(_ref) {
              var dispatch = _ref.dispatch;
              var getState = _ref.getState;
              return function (next) {// return function (store.dispatch) 
                return function (action) {
                  if (typeof action === 'function') {
                    return action(dispatch, getState);
                  }
                  return next(action);
                };
              };
            }
        }
      ])
  }
);
 
四、React-Router3、4版本的区别
来源:https://zhuanlan.zhihu.com/p/28585911
1、V3版用法
const PrimaryLayout = props =>
  <div className="primary-layout">
    <header>Our React Router 3 App</header>
    <ul>
      <li>
        <Link to="/home">Home</Link>
      </li>
      <li>
        <Link to="/user">User</Link>
      </li>
    </ul>
    <main>
      {props.children}
    </main>
  </div>;
const App = () =>
  <Router history={browserHistory}>
    <Route component={PrimaryLayout}>
      <Route path="/home" component={HomePage} />
      <Route path="/user" component={UsersPage} />
    </Route>
  </Router>;
render(<App/>, document.getElementById("root"));
2、v4版用法
const PrimaryLayout = () =>
  <div className="primary-layout">
    <header>Our React Router 4 App</header>
    <ul>
      <li>
        <Link to="/home">Home</Link>
      </li>
      <li>
        <Link to="/User">User</Link>
      </li>
    </ul>
    <main>
      <Route path="/home" exact component={HomePage} />
      <Route path="/user" component={UsersPage} />
    </main>
  </div>;
const App = () =>
  <BrowserRouter>
    <PrimaryLayout />
  </BrowserRouter>;
render(<App/>, document.getElementById("root"));

五、react-redux的实际运用
1、react-redux把redux状态和React-Router路由连起来
ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route component={PrimaryLayout}>
        <IndexRoute component={HomePage} />//在IndexRoute的所有兄弟路由都没有激活时,该组件才显示。
        <Route path="/user" component={UsersPage} />
      </Route>
    </Router>
  </Provider>,
  document.getElementById('app')
);
2、React-Redux 将所有组件分成两大类:UI 组件 和 容器组件 
(1)UI 组件:负责 UI 的呈现,即 UI本身、UI接收数据 和 UI派发行为,由用户提供。
(2)容器组件:负责管理数据和业务逻辑,由 React-Redux 自动生成。
3、UI组件StartList的定义
class StartList extends Component {
  render() {
    const { detail,close  } = this.props;
    //openDialogue 和 closeDialogue没有被解构赋值
    //this.props包含3部分:
    //(1)mapStateToProps遍历的内容
    //(2)mapDispatchToProps遍历的内容,如果该项缺失,那么将被dispatch取代
    //(3)容器标签携带的内容
    return (
      <div>
        <div>页面内容</div>
        <button onClick={ this.props.openDialogue }>{detail}</button>
      </div>
      <div>
        <div>弹窗内容</div>
        <button onClick={ this.props.closeDialogue }>{close}</button>
      </div>
    )
  }
}
4、mapStateToProps 方法定义。负责输入逻辑,即将 state 映射到 UI 组件的 props 里,
  function mapStateToProps (state){
    return {
      todos: state.todos//todos: getVisibleTodos(state.todos, state.visibilityFilter)
    }
  }
5、mapDispatchToProps 方法定义。负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
  (1)为对象时,有key和value,key为组件的属性,value为function,即action creator,返回值为action
  const mapDispatchToProps = {
    closeDialogue: function(ownProps){
      return {
        type: 'dialogue',
        filter: ownProps.filter
      }
    }
  }
  const mapDispatchToProps = {
    closeDialogue: (ownProps) => {
      type: 'dialogue',
      filter: ownProps.filter
    };
  }
  (2)为函数时,返回值为对象,有key和value,key为组件的属性,value为function,执行过程中会dispatch action
  function mapDispatchToProps (dispatch, ownProps) {//ownProps(容器组件的props对象)
    return {
      closeDialogue: function(){
        dispatch({
          type: 'dialogue',
          isShow: false
        });
      },
      openDialogue: function(){
        //1、同步请求时,此处只有下面这些代码
        //2、异步请求时,此处将 ownProps 里的部分数据作为参数,向后台发送请求,获取返回值 result,在成功的回调里执行下面这些代码
        dispatch({
          type: 'dialogue',
          isShow: true
        });
        dispatch({
          type: 'detail',
          data: result
        });
      },
    };
  }
6、容器组件LastList的定义。接收数据和派发行为,即把 mapStateToProps 和 mapDispatchToProps 传给 StartList 。
  const LastList = connect(
    mapStateToProps,
    mapDispatchToProps
  )(StartList)
  <LastList detail="详情" close="关闭"/>

//附:简单实例
class ProjectNumber extends Component {
  state = {
    visible: false
  };
  showModal(){
    const projectId = this.props.record.id;
    this.props.dispatch(getCarInfo(projectId));
    this.setState({
      visible: true,
    });
  };
  render() {
    return (
      <div>
        <span onClick={this.showModal.bind(this)}>{this.props.record}</span>
        <Modal visible={this.state.visible}></Modal>
      </div>
    );
  }
}