概述
我们先来理一理React的生命周期要领有哪些:
componentWillMount
衬着前挪用一次,这个时刻DOM构造还没有衬着。
componentDidMount
衬着完成后挪用一次,这个时刻DOM构造已衬着了。这个时刻就能够初始化其他框架的设置了,假如应用jQuery绑定事宜等等。
componentWillReceiveProps
初始化衬着不会挪用,在吸收到新的props时,会挪用这个要领。
shouldComponentUpdate
初始化衬着不会挪用,吸收到新的props或state时挪用。
componentWillUpdate
初始化衬着不会挪用,更新前挪用。
componentDidUpdate
初始化衬着不会挪用,更新后挪用。
componentWillUnmount
组件移除前挪用。
依据实行的机遇,这些要领能够分为三类。
组件挂载
组件衬着前后会实行,而且只会实行一次,看个例子
var A = React.createClass({
componentWillMount: function () {
console.log('A componentWillMount');
},
componentDidMount: function () {
console.log('A componentDidMount');
},
render: function () {
console.log('A render');
return null;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A componentWillMount
A render
A componentDidMount
componentWillMount
componentWillMount里许可我们初始化前末了一次对state举行修正,而不会触发从新衬着。
var A = React.createClass({
getInitialState: function () {
return {init: false};
},
componentWillMount: function () {
this.setState({init: true});
console.log('A componentWillMount');
},
componentDidMount: function () {
console.log('A componentDidMount');
},
render: function () {
console.log('A render:' + this.state.init);
return null;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A componentWillMount
A render:true
A componentDidMount
假如在componentDidMount中setState,效果就会是如许的。
var A = React.createClass({
getInitialState: function () {
return {init: false};
},
componentWillMount: function () {
console.log('A componentWillMount');
},
componentDidMount: function () {
this.setState({init: true});
console.log('A componentDidMount');
},
render: function () {
console.log('A render:' + this.state.init);
return null;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A componentWillMount
A render:false
A componentDidMount
A render:true
或许会有人会问了:在这个要领中
componentDidMount: function () {
this.setState({init: true});
console.log('A componentDidMount');
}
先挪用了setState,为啥不是先打印 ‘A render:true’后打印‘A componentDidMount’呢?
setState并非一个同步的要领,能够明白为异步。
这里轻易犯的毛病就是,setState完后,立时就猎取state的值做处置惩罚,效果猎取的照样老的state。
var A = React.createClass({
getInitialState: function () {
return {init: false};
},
componentWillMount: function () {
console.log('A componentWillMount');
},
componentDidMount: function () {
this.setState({init: true});
console.log('A componentDidMount:' + this.state.init);
},
render: function () {
console.log('A render:' + this.state.init);
return null;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A componentWillMount
A render:false
A componentDidMount:false
A render:true
假如想setState后猎取到更新的值,能够放在回调里
var A = React.createClass({
getInitialState: function () {
return {init: false};
},
componentWillMount: function () {
console.log('A componentWillMount');
},
componentDidMount: function () {
this.setState({init: true}, function () {
console.log('callback:' + this.state.init);
});
console.log('A componentDidMount');
},
render: function () {
console.log('A render:' + this.state.init);
return null;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A componentWillMount
A render:false
A componentDidMount
A render:true
callback:true
componentDidMount
componentDidMount衬着完成后实行一次,平常我们会在这里异步猎取数据,从新衬着页面。比方
var A = React.createClass({
getInitialState: function () {
return {data: []};
},
fetchData: function (callback) {
setTimeout(
function () {
callback([1, 2, 3]);
},
1000
);
},
componentDidMount: function () {
this.fetchData(function (data) {
this.setState({data: data});
}.bind(this));
},
render: function () {
var data = this.state.data;
return (
data.length ?
<ul>
{this.state.data.map(function (item) {
return <li>{item}</li>
})}
</ul>
:
<div>loading data...</div>
)
}
});
React.render(<A />, document.getElementById('example'));
官方文档上也说的很清晰,发起我们在componentDidMount中增加ajax,由于这是DOM已完成了初始化的衬着,在componentWillMount中猎取也能够,比方上面的例子,换在componentWillMount中猎取数据,完整OK的。然则不发起人人这么干,第一个是官方不引荐,另一个由于DOM还没有衬着,这个时刻的一些DOM操纵就会失足!
嵌套
看个父子组件的实行历程,加深对初始化衬着历程的明白。
var Child = React.createClass({
componentWillMount: function () {
console.log('Child componentWillMount');
},
componentDidMount: function () {
console.log('Child componentDidMount');
},
render: function () {
console.log('Child render');
return null;
}
});
var Parent = React.createClass({
componentWillMount: function () {
console.log('Parent componentWillMount');
},
componentDidMount: function () {
console.log('Parent componentDidMount');
},
render: function () {
console.log('Parent render');
return <Child />;
}
});
React.render(<Parent />, document.getElementById('example'));
#控制台打印
Parent componentWillMount
Parent render
Child componentWillMount
Child render
Child componentDidMount
Parent componentDidMount
组件更新
更新要领只会在组件初始化衬着完成后且触发了从新衬着的前提才会实行。更新要领同挂载要领分处组件生命周期的差别的阶段。比方一个婴儿在诞生前和诞生后,这是两个差别的阶段。
componentWillReceiveProps
组件吸收到新的props时会挪用,平常在组件嵌套中比较罕见,单一组件state变化是不会实行这个函数的。比方
var A= React.createClass({
componentWillReceiveProps: function (nextProps) {
console.log('A componentWillReceiveProps');
},
componentDidMount: function () {
this.setState({name: 'zzz'});
},
render: function () {
return null;
}
});
React.render(<A/>, document.getElementById('example'));
控制台啥也没打印
由于对组件来讲,他的props是不可变的。在看别的一个例子:
var Child = React.createClass({
componentWillReceiveProps: function (nextProps) {
console.log('Child componentWillReceiveProps');
},
render: function () {
return <div>{this.props.name}</div>;
}
});
var Parent = React.createClass({
getInitialState: function () {
return {name: 'xxx'};
},
componentDidMount: function () {
this.setState({name: 'zzz'});
},
render: function () {
return <Child name={this.state.name}/>;
}
});
React.render(<Parent />, document.getElementById('example'));
#控制台打印
Child componentWillReceiveProps
只管没有通报属性,然则要领依旧会实行,只不过nextProps是个空对象罢了。有人会问了,在Child组件当中,初始化衬着的时刻name值为‘xxx’,第二次更新的时刻name值为‘zzz’,为何会说组件的props是稳定的呢?这里不是发生变化了么?
根据我的个人明白,组件props稳定指的是在它的生命周期的阶段中,坚持稳定。比方初始化衬着的历程当中,假如在componentWillMount要领中,手动修正props,控制台就会提醒以下正告。组件更新要领主如果响应state的变化,此处更不应该去修正props。
Warning: Don't set .props.name of the React component <Child />.
Instead, specify the correct value when initially
creating the element. The element was created by Parent.
componentWillReceiveProps主如果在更新前,末了一次修正state,而不会触发从新衬着。有点相似componentWillMount,然则实行的时候不一样,比方
var Child = React.createClass({
getInitialState: function () {
return {show: false};
},
componentWillReceiveProps: function (nextProps) {
if (this.props.name !== nextProps.name) {
this.setState({show: true});
}
},
render: function () {
return this.state.show ? <div>{this.props.name}</div> : null;
}
});
var Parent = React.createClass({
getInitialState: function () {
return {name: 'xxx'};
},
componentDidMount: function () {
this.setState({name: 'xxx'});
},
render: function () {
return <Child name={this.state.name}/>;
}
});
React.render(<Parent />, document.getElementById('example'));
我们要只管防止父子组件当中都有state,如许组件的复用性就会下降,平常来讲坚持最外层的容器组件同服务器、用户交互,转变state,而子组件只担任经由过程props吸收数据,然后衬着页面。这也是官方引荐的做法。
shouldComponentUpdate
更新前挪用,返回值决议了组件是不是更新。比方
var A = React.createClass({
componentDidMount: function () {
this.setState({});
},
shouldComponentUpdate: function (nextProps, nextState) {
console.log('A shouldComponentUpdate');
return true;
},
componentWillUpdate: function () {
console.log('A componentWillUpdate');
},
componentDidUpdate: function () {
console.log('A componentDidUpdate');
},
render: function () {
console.log('A render');
return null ;
}
});
React.render(<A />, document.getElementById('example'));
#控制台打印
A render
A shouldComponentUpdate
A componentWillUpdate
A render
A componentDidUpdate
第一个render是初始化。组件会将render要领的返回值同已有的DOM构造比较,只更新有更改的的部份,这个历程是须要消费时候的,在这个要领中我能够决议是不是须要更新组件,从而削减机能的消耗。
this.forceUpdate()不会实行shouldComponentUpdate要领,由于是强迫更新,不会由于shouldComponentUpdate的返回值决议是不是更新,所以跳过该要领。别的还须要注意的是,this.forceUpdate()挪用会致使该组件的shouldComponentUpdate不实行,对子组件的shouldComponentUpdate要领没有影响。
componentWillUpdate、componentDidUpdate
组件更新前后实行,没办法决议组件是不是更新,只能举行些非状况的操纵,个人感觉用处不太显著。
组件更新的全部历程
var Child = React.createClass({
componentWillReceiveProps: function () {
console.log('Child componentWillReceiveProps');
},
shouldComponentUpdate: function (nextProps, nextState) {
console.log('Child shouldComponentUpdate');
return true;
},
componentWillUpdate: function () {
console.log('Child componentWillUpdate');
},
componentDidUpdate: function () {
console.log('Child componentDidUpdate');
},
render: function () {
console.log('Child render');
return null ;
}
});
var Parent = React.createClass({
componentDidMount: function () {
this.setState({});
},
render: function () {
return <Child />;
}
});
React.render(<Parent />, document.getElementById('example'));
#控制台打印
Child render
Child componentWillReceiveProps
Child shouldComponentUpdate
Child componentWillUpdate
Child render
Child componentDidUpdate
第一个render是初始化挪用的,不是更新的历程。
移除
componentWillUnmount
组件被移除前挪用,这里能够做一些消灭事情,比方消灭内存,消除事宜的监听等等。
var A = React.createClass({
componentDidMount: function () {
this.interval = setInterval(
function () {
console.log('running');
},
100
);
},
handleClick: function () {
React.unmountComponentAtNode(document.getElementById('example'));
},
componentWillUnmount: function () {
clearInterval(this.interval);
},
render: function () {
return <button onClick={this.handleClick}>click</button>;
}
});
React.render(<A />, document.getElementById('example'));