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')