17、react之表格和分页组件、redux实际运用、react-redux实际应用。react虚拟DOM、优缺点、传值、组件的3个部分、ref、key、

200 阅读13分钟

17、react之表格和分页组件、redux实际运用、react-redux实际应用。react虚拟DOM、优缺点、传值、组件的3个部分、ref、key、约束组件、setState、状态提声、生命周期及其2种组件实例、react.15.6.0.js源码外框、portal插槽、React.Children插槽、前端路由及触发条件

一、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 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)
      }
    });
  }
  addThisColumnRowspan(columns,maxRowspan){
    var that=this;
    columns.forEach(function(item,index) {
      if(item.children){
        item.thisRowspan=1;
        that.addThisColumnRowspan(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,index) {
      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.addThisColumnRowspan(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",width:"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={{width:"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、createStore实际运用(伪代码)
function createStore(reducer, preloadedState, enhancer) {
  return enhancer(createStore)(reducer, preloadedState);
}
createStore(
  rootReducer,
  preloadedState,
  compose(functionA,functionB,functionC,functionD)//enhancer
)
function functionA(createStore3) {//7、接收functionB的返回值createStore3
  return function createStore4(reducer4, preloadedState4, enhancer4) {//8、返回值为createStore4
    //9、实际执行createStore4(reducer, preloadedState),此处加工参数reducer, preloadedState,传给下面的createStore3
    var store3=createStore3(reducer3, preloadedState3, enhancer3);
    //16、此处对createStore3的返回值store3进行加工,下面return的是createStore4的返回值,也是最终的返回值
    return {
      dispatch: dispatch3,
      subscribe: subscribe3,
      getState: getState3,
      replaceReducer: replaceReducer3
    }
  }
};
function functionB(createStore2) {//5、接收functionC的返回值createStore2
  return function createStore3(reducer3, preloadedState3, enhancer3) {//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(reducer1, preloadedState1, enhancer1);
    //14、此处对createStore1的返回值store1进行加工,下面return的是createStore2的返回值
    return {
      dispatch: dispatch1,
      subscribe: subscribe1,
      getState: getState1,
      replaceReducer: replaceReducer1
    }
  }
};
function functionD(createStore0) {//1、实际执行functionD(createStore)
  return function createStore1(reducer1, preloadedState1, enhancer1) {//2、返回值为createStore1
  //12、此处加工参数,传给下面的createStore0
    var store0=createStore0(reducer0, preloadedState0, enhancer0);
    //13、此处对createStore0的返回值store0进行加工,下面return的是createStore1的返回值
    return {
      dispatch: dispatch0,
      subscribe: subscribe0,
      getState: getState0,
      replaceReducer: replaceReducer0
    }
  }
};
2、compose实际运用(包裹但不执行其参数)
var arrayFunction = [functionA, functionB, functionC, functionD];
function compose(arrayFunction) {
  return arrayFunction.reduce(function (a, b) {
    //1、包裹
    //第1次执行时,a是functionA,b是functionB,执行结果为functionOne
    //第2次执行时,a是functionOne,b是functionC,执行结果为functionTwo
    //第3次执行时,a是functionTwo,b是functionD,执行结果为functionThree
    //2、给functionThree传参并执行
    //第1次执行时,functionThree(createStore0),functionD执行,返回createStore1
    //第2次执行时,functionTwo(createStore1),functionC执行,返回createStore2
    //第3次执行时,functionOne(createStore2),functionB执行,返回createStore3
    //第4次执行时,functionA(createStore3),返回createStore4
    //3、给createStore4传参并执行,进而给createStore3、createStore2、createStore1、createStore0传参并执行
    //4、给createStore0的返回值加工,进而给createStore1、createStore2、createStore3、createStore4的返回值加工,生成最终store
    return function () {
      return a(b.apply(undefined, arguments));
    };
  })
}
compose(arrayFunction)(createStore)(reducer, preloadedState);
//第一次循环后的结果如下
function functionOne() {
  return functionA(functionB.apply(undefined, arguments));
}
//第二次循环后的结果如下
function functionTwo() {
  return functionOne(functionC.apply(undefined, arguments));
}
//第三次循环后的结果如下
function functionThree() {
  return functionTwo(functionD.apply(undefined, arguments));
}
//functionThree实际上就是enhancer,通过functionThree(createStore)(reducer, preloadedState)起作用。

3、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
}

4、相关源码解析
(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) {
    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: false }//action匹配成功,state的引用就改变了
      //     default:
      //       return state;//action匹配不成功,state的引用就不改变了
      //   }
      // }
    }
    //遍历结束,一次性返回结果。
    return hasChanged ? nextState : state;
  };
}
(5)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);
    };
  };
}
(6)其它
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
    })
  }
}
(7)
(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-redux实际应用
1、实际运用情形
react-redux把redux状态和React-Router路由连起来 注意:前端单页面应用,就是把状态和路由连起来,同时注意嵌套
class App extends Component {
  render() {
    return(
        <div>
          {this.props.children}  
        </div>
    )
  }
}
let routes = (
  // Route 组件 无 path,直接渲染,有 path,匹配渲染
  <Route component={App}>
    // App 组件下的 <div>{this.props.children}</div> 用来放置 Public (为空)及其内部第一个被匹配的组件     
    <Route component={Public}>/* 以下内容渲染在 Public 组件下的 <div>{this.props.children}</div> 里*/
      //以下写法一:
      <Route path="/" component={index}/>
      <Route path="/carDetail" component={carDetail}/> //通过browserHistory.push('/carDetail')渲染
      //以下写法二:
      <Route path="/" component={index}>
        <Route path="/carDetail" component={carDetail}/>
      </Route>   
    </Route> 
    // 如果 Public 下,没有组件匹配成功,那么 Public 为空,不显示;NotFound 会被渲染 
    <Route path="*" component={NotFound} />//通过browserHistory.push('carDetail')渲染
  </Route>
)
ReactDOM.render(
  <Provider store={store}>
    <div>
      <Router onUpdate={() => window.scrollTo(0, 0)} history={browserHistory} children={routes}></Router>
    </div>
  </Provider>,
  document.getElementById('app')
)
document.body.removeChild(document.getElementById('svganimate'));
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>
    );
  }
}
 
一、真实DOM和虚拟DOM
1、真实DOM,用户每次操作DOM(文档对象模型),都会引起BOM(浏览器对象模型)重新渲染
2、虚拟DOM,用户每次操作DOM(文档对象模型),都会改变虚拟DOM(堆内存的一个对象),最后一次性地渲染到BOM上。

二、React的缺点和优点  
1、缺点:
(1)React本身只是一个V,不是一个完整的框架,不是一套完整的框架
(2)需要加上React-Router和React-Redux才能成为一套完整的框架  
2、优点:
(1)单向数据流动;
(2)虚拟DOM取代物理DOM作为操作对象;
(3)用JSX语法取代HTML模板,在JavaScript里声明式地描述UI。  

三、react组件间的传值
(1)react父组件向子组件传值:属性传参
(2)react子组件向父组件传值:实例是一个对象,在父组件定义一个函数,给这个函数绑定父组件的this,在这个函数的函数体里,将这个函数的参数赋值给父组件实例的一个属性;然后通过属性传参的方式把这个函数传给子组件。在子组件里,给这个函数传参并执行,实现子组件向父组件传值!

四、React 组件
1、React 组件基本上由 3 个部分组成——属性(props)、状态(state)以及生命周期方法
2、React 组件一旦接收到的参数(即props)或自身状态有所改变,React 组件就会执行相应的生命周期方法,最后渲染。
3、整个过程完全符合传统组件所定义的组件职责(“属性更新”与“状态改变”)。
4、以上内容来自《深入React技术栈》第18和30页。

五、ref可以设置回调函数
1、<input type="text" ref="myInput"/>
this.refs.myInput.value ="22"; //this.$refs.myInput.value ="22"  减少获取dom节点的消耗
2、ref属性可以设置为一个回调函数,这也是官方强烈推荐的用法;这个函数执行的时机为:
3、组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例。
4、组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null,以确保内存泄露。

六、为什么不能用index做key? 
1、react会根据key来决定是否重新构建该组件
2、删除和添加操作,会使一个组件使用另一个的组件的index,进而key,进而data

七、约束性组件和非约束性组件(受控组件和非受控组件)
1、约束性组件
<input type="text" value={this.state.name} onChange={this.handleChange} />
handleChange: function(e) {
  this.setState({name: e.target.value});
}
//用户输入内容A>触发onChange事件>handleChange中设置state.name="A"渲染input使他的value变成A
2、非约束性组件
<input type="text" defaultValue="a" />//用户输入A -> input 中显示A 
3、React 把 input,textarea 和 select 三个组件做了抽象和封装,他们有统一的 value 属性 和 onChange 事件。
<input type='text' name='intro' id='intro' value={this.state.email} onChange={this.handleEmail} />
<textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} />
<textarea type='text' name='intro' id='intro' value={this.state.intro} onChange={this.handleIntro} />
4、checkbox改变的不是 value ,而是 checked 状态。
<input type='radio' name='gender' checked={this.state.male==='MALE'} onChange={this.handleGender} value='MALE' />
<input type='radio' name='gender' checked={this.state.male==='FEMALE'} onChange={this.handleGender} value='FEMALE' />
handleGender(e){
  this.setState({
    male:e.target.value
  })
}

八、setState
1、this.setState接收两种参数
(1)对象+函数(可选):传入的对象浅层合并到新的state中
(2)函数+函数(可选):第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调。
2、this.setState何时同步何时异步?
(1)异步,由react控制的事件处理程序
(2)同步,react控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。
(3)大部分开发中用到的都是react封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。
3、this.setState何时渲染
(1)死循环:this.setState执行时,会根据_compositeLifeCycleState是否为null,来决定是否重新渲染,因此在有的生命周期里,会产生死循环。
(2)只生效一次:this.setState最后执行队列时,先用变量获取并存储state,后来直接修改变量,最后把变量赋值给state,页面渲染。
handleClick() {
  //const count = this.state.count 下面 this.setState 多次执行,但只生效一次。因为似乎存在此行代码。
  this.setState({
    count: count + 1
  })
  this.setState({
    count: count + 1
  })
}

九、状态提声(父组件的函数作为属性传给子组件)
1、在父组件的constructor中定义状态
2、在父组件的方法中执行this.setState({})
3、把父组件的方法作为属性fromParent传给子组件
4、在子组件的方法中加上this.props.fromParent(e.target.value);
5、触发子组件的事件,执行子组件的方法,改变父组件的状态


十、React组件生命周期
A、ES5写法
1、ES5写法之实例化时期
(1)getDefaultProps
(2)getInitialState
(3)componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态
执行到这里,生命状态会被重置为null
(4)render//执行setState会发起updateComponent导致死循环
(5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期
2、ES5写法之存在期
(1)componentWillReceiveProps//执行setState会合并到状态中;
执行到这里,生命状态会被重置为null
(2)shouldComponentUpdate//执行setState会发起updateComponent导致死循环
(3)componentWillUpdate//执行setState会发起updateComponent导致死循环
(4)render//执行setState会发起updateComponent导致死循环
(5)componentDidUpdate//可以有条件地执行setState
3、ES5写法之销毁时期
(1)componentWillUnmount//等待页面卸载,改变state没意义。
B、ES6写法(旧,React v16.3-)
1、ES6写法之实例化时期
(1)static
(2)constructor
(3)componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态
执行到这里,生命状态会被重置为null
(4)render//执行setState会发起updateComponent导致死循环
(5)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期
2、ES6写法之存在期
(1)componentWillReceiveProps//执行setState会合并到状态中;
执行到这里,生命状态会被重置为null
(2)shouldComponentUpdate//执行setState会发起updateComponent导致死循环
(3)componentWillUpdate//执行setState会发起updateComponent导致死循环
(4)render//执行setState会发起updateComponent导致死循环
(5)componentDidUpdate//可以有条件地执行setState
3、ES6写法之销毁时期
(1)componentWillUnmount//等待页面卸载,改变state没意义。
C、ES6写法(新,React v16.4+)
1、ES6写法之实例化时期
(1)constructor
(2)static getDerivedStateFromProps//执行setState会合并到初始化状态中;获取从属性生成的状态
执行到这里,生命状态会被重置为null
(3)render//执行setState会发起updateComponent导致死循环
(4)componentDidMount//执行setState会导致更新;这是发起异步请求去API获取数据的绝佳时期
2、ES6写法之存在期
(1)static getDerivedStateFromProps//执行setState会合并到状态中;
执行到这里,生命状态会被重置为null
(2)shouldComponentUpdate//执行setState会发起updateComponent导致死循环
(3)render//执行setState会发起updateComponent导致死循环
(4)getSnapshotBeforeUpdate//执行setState会发起updateComponent导致死循环
(5)componentDidUpdate//可以有条件地执行setState
3、销毁时期
(1)componentWillUnmount//等待页面卸载,改变state没意义。
4、另外====
(1)getDerivedStateFromProps//返回一个对象来更新state,如果返回 null 则不更新任何内容。

十一、React组件生命周期实例(es5写法)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React实例</title>
<script src="https://lib.baomitu.com/react/15.6.1/react.js"></script>
<script src="https://lib.baomitu.com/react/15.6.1/react-dom.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
var Button = React.createClass({
  setNewNumber(number,event) {
    console.log(number)
    console.log(event)
    this.setState({number: this.state.number + 1})
  },
  //一、以下实例化时期
  //1.getDefaultProps
  getDefaultProps() {
    return { name: "计数器" };
  },
  //2.getInitialState
  getInitialState() {
    return{number: 0};
  },
  render() {
    return (
      <div>
        <button onClick = {this.setNewNumber.bind(null,this.state.number,event)}>点击{this.props.name}</button>
        <Text myNumber = {this.state.number}></Text>
      </div>
    );
  }
})
var Text = React.createClass({
  //一、以下实例化时期
  //1.getDefaultProps
  getDefaultProps() {
    console.log("1.getDefaultProps 获取默认属性");
    return { };
  },
  //2.getInitialState
  getInitialState() {
    console.log("2.getInitialState 获取初始状态");
    return { };
  },
  //3.componentWillMount
  componentWillMount() {
    console.log("3.componentWillMount 此组件将要被渲染到目标组件内部");
  },
  //4.render在下方
  //5.componentWillMount
  componentDidMount() {
    console.log("5.componentWillMount 此组件已经被渲染到目标组件内部");
  },
  //二、以下存在时期
  //6.componentWillReceiveProps
  componentWillReceiveProps() {
    console.log("6.componentWillReceiveProps 此组件将要收到属性");
  },
  //7.shouldComponentUpdate
  shouldComponentUpdate(newProps, newState) {
    console.log("7.shouldComponentUpdate 组件是否应该被更新");
    return true;
  },
  //8.componentWillUpdate
  componentWillUpdate() {
    console.log("8.componentWillUpdate 组件将要被更新");
  },
  //9.render在下方
  //10.componentDidUpdate
  componentDidUpdate() {
    console.log("10.componentDidUpdate 组件已经更新完成");
  },
  //三、以下销毁时期
  //11.componentWillUnmount
  componentWillUnmount() {
    console.log("11.componentWillUnmount 组件将要销毁");
  },
  render() {
    return (
      <div>
        <h3>{this.props.myNumber}</h3>
      </div>
    );
  }
})
ReactDOM.render(
   <div>
      <Button />
   </div>,
  document.getElementById('example')
);
</script>

</body>
</html>
 
十二、React组件生命周期实例(es6写法)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React实例</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>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
class Button extends React.Component {
  name="计算器";//这样也行,二选一
  state = {number: 0};//这样也行,二选一
  constructor(props) {
      super(props);
      this.name="计算器";//这样也行,二选一
      this.state = {number: 0};//这样也行,二选一
      this.setNewNumber = this.setNewNumber.bind(this);
  };
  setNewNumber(number,event) {
    console.log(number)
    console.log(event)
    this.setState({number: this.state.number + 1})
  };
  render() {
      return (
         <div>
            <button onClick = {this.setNewNumber.bind(this,this.state.number,event)}>点击{this.name}</button>
            <Text myNumber = {this.state.number}></Text>
         </div>
      );
    }
}
class Text extends React.Component {
  //一、以下实例化时期
  //1.和2.constructor
  constructor(props) {
    super(props);
    console.log("2.constructor 获取初始状态");
  }
  //3.componentWillMount
  componentWillMount() {
    console.log("3.componentWillMount 此组件将要被渲染到目标组件内部");
  }
  //4.render在下方
  //5.componentWillMount
  componentDidMount() {
    console.log("5.componentWillMount 此组件已经被渲染到目标组件内部");
  }
  //二、以下存在时期
  //6.componentWillReceiveProps
  componentWillReceiveProps() {
    console.log("6.componentWillReceiveProps 此组件将要收到属性");
  }
  //7.shouldComponentUpdate
  shouldComponentUpdate(newProps, newState) {
    console.log("7.shouldComponentUpdate 组件是否应该被更新");
    return true;
  }
  //8.componentWillUpdate
  componentWillUpdate() {
    console.log("8.componentWillUpdate 组件将要被更新");
  }
  //9.render在下方
  //10.componentDidUpdate
  componentDidUpdate() {
    console.log("10.componentDidUpdate 组件已经更新完成");
  }
  //三、以下销毁时期
  //11.componentWillUnmount
  componentWillUnmount() {
    console.log("11.componentWillUnmount 组件将要销毁");
  }
  render() {
    return (
      <div>
        <h3>{this.props.myNumber}</h3>
      </div>
    );
  }
}
ReactDOM.render(
   <div>
      <Button />
   </div>,
  document.getElementById('example')
);
</script>
</body>
</html>
来源 https://www.runoob.com/try/try.php?filename=try_react_life_cycle2
 

十三、React.15.6.0源码外框
1、理解一
/**
 * React v15.6.0
 */
(function (allFn) {
  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = allFn()
  } else if (typeof define === "function" && define.amd) {
    define([], allFn)
  } else {
    var tempGlobal;
    if (typeof window !== "undefined") {
      tempGlobal = window
    } else if (typeof global !== "undefined") {
      tempGlobal = global
    } else if (typeof self !== "undefined") {
      tempGlobal = self
    } else {
      tempGlobal = this
    }
    tempGlobal.React = allFn()
  }
})(function () {
  var define, module, exports;
  return (function outerFn(first, second, third) {
    function recursion(number) {
      if (!second[number]) {
        if (!first[number]) {
          var error = new Error("Cannot find module '" + number + "'");
          throw error.code = "MODULE_NOT_FOUND", error
        }
        var module = second[number] = {
          exports: {}
        };
        first[number][0].call(module.exports, function (key) {
          var value = first[number][1][key];
          return recursion(value ? value : key)
        }, module, module.exports, outerFn, first, second, third)
      }
      return second[number].exports
    }
    for (var number = 0; number < third.length; number++) recursion(third[number]);
    return recursion
    //以上两行可以合并为下面一行
    //return recursion(third[0]); 
    //同时下面的"(48)" 应当删掉 
  })({ 2: [function (_dereq_, module, exports) { console.log(_dereq_) }, { "25": 25, "30": 30 }], }, {}, [16])(16)
});
2、理解二
/**
* react v15.6.0
*/
(function (f) {
  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = f()
  } else if (typeof define === "function" && define.amd) {
    define([], f)
  } else {
    var g;
    if (typeof window !== "undefined") {
      g = window
    } else if (typeof global !== "undefined") {
      g = global
    } else if (typeof self !== "undefined") {
      g = self
    } else {
      g = this
    }
    g.React = f()
  }
})(function () {
  var define, module, exports;
  return (function e(t, n, r) {
  ///////////////////////////////////////////////////////////////////////////
    function fn(num) {
      //fn的作用:
      //(1)定义接收容器。
      //(2)填充接收容器,即给接收容器m下的exports属性和第2个实参对象n下的[num]属性下的exports属性,增加属性值(对象类型);
      //(3)暴露接收容器的内容。
      //t[num][0]的作用:填充接收容器。
      //function(e){}的作用:填充接收容器时,获取所需依赖。
      if (!n[num]) {
        if (!t[num]) {/* 抛错 */}
        var m = n[num] = { exports: {} };
        t[num][0].call(m.exports, function (e) { 
          var number = t[num][1][e];
          return fn(number ? number : e)
        }, m, m.exports)
      }
      return n[num].exports//在react实例化的过程中,这行代码不但因获取依赖而多次执行,而且还因获取react实例而最后执行。
    }
  ///////////////////////////////////////////////////////////////////////////  
    for (var i = 0; i < r.length; i++){fn(r[i])/* fn(16)第1次执行,执行结果没有变量接收 */};
    return fn // 执行到这,整个逻辑就快结束了
  })(
      /* 第1个实参 */{1:[function(_dereq_, module, exports){},{"5":5,"6":6}],2:[],3:[],4:[],5:[],...},
      /* 第2个实参 */{/* 存储依赖 */}, 
      /* 第3个实参 */[16]
    )(16)// fn(16)第2次执行,因为n[num]为真,所以直接返回n[num].exports并被挂在g.React上 
});
 
十四、portal插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React插槽实例</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>
</head>
<body>
  <div id="container"></div>
  <div id="outer"></div>
</body>
<script type="text/babel">
const container = document.getElementById('container');
const outer = document.getElementById('outer');
class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.div = document.createElement('div');
  }
  componentDidMount() {
    outer.appendChild(this.div);
  }
  componentWillUnmount() {
    outer.removeChild(this.div);
  }
  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.div
    );
  }
}
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }
  render() {
    return (
      <div onClick={this.handleClick}>
        <div>{this.state.clicks}</div>
        <div><button>Click</button></div>
        <Modal>
          <div>
            <button className="modal">Click</button>
          </div>
        </Modal>
      </div>
    );
  }
}
ReactDOM.render(<Parent />, container);
</script>

十五、React.Children插槽
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React插槽实例</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>
</head>
<body>
  <div id="container"></div>
  <div id="outer"></div>
</body>
<script type="text/babel">
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick(event) {
    console.log(event)
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }
  render() {
    var that = this;
    return (
      <div>
        <div>{this.state.clicks}</div>
        <div><button onClick={this.handleClick}>clicks</button></div>
        <ul>
          {
            React.Children.map(this.props.children,function(item,index){
              if(index !=1){
                return <li onClick={that.handleClick}>{item}</li>
              }else{
                return <li onClick={that.handleClick}>{item}---{index+1}</li>
              }
            })
          }
        </ul>
      </div>
    );
  }
}
ReactDOM.render(<Parent>
  <span style={{cursor:'pointer',userSelect: 'none'}}>插槽一</span>
  <span style={{cursor:'pointer',userSelect: 'none'}}>插槽二</span>
  <span style={{cursor:'pointer',userSelect: 'none'}}>插槽三</span>
</Parent>, document.getElementById('container'));
</script>
</html>

十六、前端路由:url有变化,但不向后台发送请求,
1、它的作用是 
(1)记录当前页面的状态; 
(2)可以使用浏览器的前进后退功能; 
2、hash模式帮我们实现这两个功能,因为它能做到: 
(1)改变url且不让浏览器向服务器发出请求; 
(2)监测 url 的变化; 
(3)截获 url 地址,并解析出需要的信息来匹配路由规则。 
3、history 模式改变 url 的方式会导致浏览器向服务器发送请求。
4、react前端路由触发条件
(1)<Link to={item.url}>{item.location}</Link>                         
(2)browserHistory.push('/aaa')