• 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;
        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;
        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 id="container"></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)}/>)
    }
    class TablePage extends React.Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.initThead = [];
        this.dataIndexs = [];//把表头每一栏最后一项的dataIndex放到这里,作为表头和表体关联的依据
        this.maxRowspan = 1;//表头最大跨行数
        if(props.giveParentClickAllPages){
          props.giveParentClickAllPages(this)
        } 
      }
      getDataIndexsAndMaxRowspan(columns,maxRowspan){
        var that=this;
        columns.forEach(function(item,index) {
          if(item.children){
            maxRowspan+=1;
            if(maxRowspan>that.maxRowspan) that.maxRowspan = maxRowspan;
            that.getDataIndexsAndMaxRowspan(item.children,maxRowspan)
          }else{
            that.dataIndexs.push(item.dataIndex)
          }
        });
      }
      addEachColumnRowspan(columns,maxRowspan){
        var that=this;
        columns.forEach(function(item,index) {
          if(item.children){
            item.thisRowspan=1;
            that.addEachColumnRowspan(item.children,maxRowspan-1)
          }else{
            item.thisRowspan=maxRowspan;
          }
        });
      }
      addEachColumnColspan(columns){
        var that=this;
        columns.forEach(function(item) {
          if(item.children){
            if(!item.thisColspan)that.addThisColumnColspan(item,item.children)
          }else{
            item.thisColspan=1;
          }
        });
      }
      addThisColumnColspan(item,children){
        var that=this;
        children.forEach(function(child) {
          var thisChildren= child.children;
          if(thisChildren){
            that.addThisColumnColspan(item,thisChildren);
            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,
        })
      }
      clickAllPages(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.getDataIndexsAndMaxRowspan(columns,this.maxRowspan);
        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可传入属性
    //1、numOptions:每页显示多少项
    //2、text:总项数前后的说明文字
    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 allPagesNum = this.props.divideSource.allPagesNum;
        var nowPageNum = this.props.divideSource.nowPageNum;
        if (allPagesNum >= 1 && allPagesNum <= 10) {
          for (var i = 1; i <= allPagesNum; i++) {
            numButton.push(<button key={i} style={i==nowPageNum?{color:'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} style={{color:'red'}} onClick={this.clickButton.bind(this,nowPageNum)}>{nowPageNum}</button>);
          } else {
            for (i = 1; i <= nowPageNum; i++) {
              numButton.push(<button key={i} style={i==nowPageNum?{color:'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>)
            }
          }
        }
        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>)
        }
        return (
          <div style={{display:'block',display:"flex","1000px",marginTop:"20px"}}>
            <div style={{display:"flex"}}>
              <button style={{marginRight:"5px"}} disabled={this.props.divideSource.nowPageNum===1} onClick={this.clickButton.bind(this,'front')}>上一页</button>
              <div>{ numButton }</div>
              <button disabled={this.props.divideSource.nowPageNum===this.props.divideSource.allPagesNum} onClick={this.clickButton.bind(this,'back')}>下一页</button>
            </div>
            <div style={{display:"flex", flex:1, justifyContent:"flex-end"}}>
              <div style={{marginRight:"15px"}}>
                <span>转到第</span>
                <input id='inputQC' key={this.props.divideSource.nowPageNum==1?Math.random():'key'} type="text" style={{"30px",margin:"0 5px"}} ref="input" onChange={this.inputChange.bind(this)} onKeyDown={this.onKeyDown.bind(this,event)} defaultValue={this.props.divideSource.inputValue}/>
                <span>页</span>
                <button style={{margin:"0 5px"}} onClick={this.clickButton.bind(this,'leap')}>Go</button>
              </div>
              <div>
                <span>每页显示</span>
                <select style={{margin:"0 5px"}} ref="select" defaultValue={this.props.divideSource.eachPageItemsNum||10} onChange={this.pageNumLeap.bind(this)}>
                  { selectOption }
                </select>
                <span>{(this.props.text&&this.props.text.unit)||""}</span>
              </div>
              <div>
                <span>{this.props.text&&this.props.text.frontMoreText}</span>
                <span>{(this.props.text&&this.props.text.totalText)||""}</span>
                <span>{this.props.divideSource.allItemsNum||0}</span>
                <span>{(this.props.text&&this.props.text.totalUnit)||""}</span>
                <span>{this.props.text&&this.props.text.backMoreText}</span>
              </div>
            </div>   
          </div>
        )
      }
    }
    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: '未启用,无选择项!', 
          },
        };
      };
      componentWillMount() { 
        this.divideClick(1,10)
      }
      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),
            age:(i+11)+'',
            street:''+(i+3)+'大街',
            building:(i+2)+'号楼',
            number:((i+1)+'0'+5)+'',
            companyAddress:'公司地址'+(i+5),
            companyName:'公司名称'+(i+6),
            gender:(i%2===0?'':'')
          };
          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,
        })
      }
      getChildClickAllPages(that){
        this.childRef=that;//把子组件的实例赋值给父组件的childRef属性。非常重要!!!
      }
      clickAllPages(dataSource){
        this.childRef.clickAllPages(dataSource)//在父组件里调用子组件的方法。非常重要!!!
      }
      changeValue(key,event){
        this.setState({
          filter:{...this.state.filter,[key]:event.target.value}
        })
      }
      render() {
        var columns = [
          {
            title: '',
            dataIndex: 'checkQC',
          },
          {
            title: '序号',
            dataIndex: 'order',
          },
          {
            title: 'Name',
            dataIndex: 'name',
          },
          {
            title: 'Person',
            children: [
              {
                title: 'Age',
                dataIndex: 'age'
              },
              {
                title: 'Family Address',
                children: [
                  {
                    title: 'Street',
                    dataIndex: 'street'
                  },
                  {
                    title: 'Block',
                    children: [
                      {
                        title: 'Building',
                        dataIndex: 'building'
                      },
                      {
                        title: 'Door No.',
                        dataIndex: 'number'
                      },
                    ],
                  },
                ],
              },
            ],
          },
          {
            title: 'Company',
            children: [
              {
                title: 'Company Address',
                dataIndex: 'companyAddress'
              },
              {
                title: 'Company Name',
                dataIndex: 'companyName'
              },
            ],
          },
          {
            title: 'Gender',
            dataIndex: 'gender'
          },
        ];
        var {dataSource,divideSource,checkSource,filter}={...this.state}; 
        return (
          <div> 
            <div>以下是过滤示例</div>
            <div style={{'border':'1px solid #cbcbcb','width':'980px','padding':'10px','margin':'6px 0'}}>
              <div style={{'display':'flex'}}>
                <div style={{'width':'212px'}}> 
                  <label style={{'paddingRight':'4px'}}>输入框示例</label>
                  <input type='text' placeholder='请输入' onChange={this.changeValue.bind(this,'input')}  style={{'border':'1px solid #cbcbcb','width':'100px'}}/>
                </div>
                <div style={{'width':'174px'}}> 
                  <label style={{'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 style={{'width':'500px'}}> 
                  <span>过滤条件为:{JSON.stringify(this.state.filter)}</span>   
                  <button onClick={this.divideClick.bind(this,3,10)}>过滤</button>
                  <button onClick={this.divideClick.bind(this,1,10)}>刷新</button>
                </div>
              </div>
            </div>
            <div style={{'paddingBottom':'4px'}}>
              <span style={{'paddingRight':'10px'}}><TwoImg isTrue={checkSource.isSelectAllPages && checkSource.allExcludedIds.length===0} clickImg={this.clickAllPages.bind(this,dataSource)}/></span>     
              <span>{checkSource.textAllPages}</span>
            </div>
            <TablePage 
              idKey='id' 
              columns={columns}
              dataSource={dataSource}
              checkSource={checkSource}
              checkboxClick={this.checkboxClick.bind(this)}
              giveParentClickAllPages={this.getChildClickAllPages.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),返回createStore42)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":
          //       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>
        );
      }
    }
  • 相关阅读:
    阶段性总结---初始阶段
    微信授权以及微信支付所遇到的坑(完善)
    CSS3学习
    Playing with String(codeforces 305E)
    Treblecross(uva 10561)
    序列操作(bzoj 1858)
    传送带(bzoj 1857)
    字符串(bzoj 1856)
    刷题比赛(洛谷 1707)
    大楼(bzoj 2165)
  • 原文地址:https://www.cnblogs.com/gushixianqiancheng/p/13882195.html
Copyright © 2020-2023  润新知