生命周期方法
执行顺序
首次挂载组件时,按如下顺序执行。
- getDefaultProps
- getInitialState
- componentWillMount
- render
- componentDidMount
上面的getDefaultProps和getInitialState方法,都是只执行一次。
// 返回值可以通过 this.props 读取
getDefaultProps: function(){
return { /* something here */};
}
// 返回值可以通过 this.state 读取
getInitialState: function(){
return { /* something here */};
}
componentDidMount方法已经可以读取组件生成的 DOM。如果要与 DOM 互动,应该就在这个方法里面,而不是在render方法里面。
卸载组件时,按如下顺序执行。
- 卸载组件
- componentWillUnmount
重新挂载组件时,按如下顺序执行。
- getInitialState
- componentWillMount
- render
- componentDidMount
再次渲染组件时,组件接受到父组件传来的新参数,按如下顺序执行。
- 更新 Props
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
如果组件自身的state更新了,按如下顺序执行。
- 更新 state
- shouldComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
componentDidUpdate
componentDidUpdate方法在每次组件重新渲染(render方法)后执行。
该方法可以操作组件所在的 DOM,用于操作数据已经更新之后的组件。
上面代码在组件每次重新渲染后,会执行this.scrollElement方法。
这里有一个问题,如果this.scrollElement里面需要操作 DOM,这时很可能 DOM 还没有完全生成。因此,可以使用requestAnimationFrame或setTimeout方法,保证操作时 DOM 已经生成。
scrollElement: function() {
var _this = this;
setTimeout(function () {
window.requestAnimationFrame(function() {
var node = _this.getDOMNode();
if (node !== undefined) {
node.scrollTop = node.scrollHeight;
}
})
}, 0)
},
componentDidMount: function() {
this.scrollElement();
},
shouldComponentUpdate
shouldComponentUpdate方法会在每次重新渲染(render方法)之前,自动调用。它返回一个布尔值,决定是否应该进行此次渲染。默认为true,表示进行渲染,如果为false,就表示中止渲染。
下面是父元素组件Color的代码。
getInitialState: function () {
return {
colors: new Immutable.List(this.props.colors)
};
},
_addColor: function (newColor) {
this.setState({
colors = this.state.colors.push(newColor)
});
},
render: function () {
return (
<div>
<ColorList colors={this.state.colors} />
<ColorForm addColor={this._addColor} />
</div>
);
}
上面代码中,父组件Color向子组件ColorList传入参数this.state.colors。每当父组件的this.state.colors变动时,子组件就会重新渲染,这时子组件的shouldComponentUpdate就会自动调用。
shouldComponentUpdate: function (nextProps, nextState) {
return nextProps.colors !== this.props.colors;
}
上面是子组件的shouldComponentUpdate方法,它接受两个参数,第一个是本次传入的新参数对象nextProps,第二个是新的状态对象nextState。在方法内部,this.props和this.state表示当前(没有重新渲染之前)的参数对象和状态对象。
注意,shouldComponentUpdate方法默认返回true,这意味着即使state和props没有改变,只要调用this.setState,就会导致组件重新渲染。
componentWillUpdate
一旦shouldComponentUpdate返回true,componentWillUpdate就会执行,主要用于为即将到来的更新做准备工作。
componentWillUpdate: function(nextProps, nextState){
// perform any preparations for an upcoming update
}
注意,这个方法之中不应该调用this.setState,因为它本身不应该触发更新。
componentWillReceiveProps
componentWillReceiveProps方法在父组件每次重新传给当前组件参数时调用,它在当前组件的render()之前调用。组件的第一次渲染不会调用这个方法。
它只在父组件更新props时执行,当前组件本身调用setState而引发重新渲染,是不会执行这个方法的。在此方法中调用setState也不会二次渲染的。componentWillReceiveProps可以根据已有的props和刚刚传入的props,进行state的更新,而不会触发组件的重新更新。
在componentWillReceiveProps之中,可以调用setState方法。而componentWillUpdate是用来回应state变化的方法。
componentWillReceiveProps在shouldComponentUpdate和componentWillUpdate之前调用。
这个方法可以用来在render()调用之前,对props进行调整,然后通过this.setState()设置state。老的props可以用this.props拿到。
componentWillReceiveProps: function(nextProps) {
this.setState({
likesIncreasing: nextProps.likeCount > this.props.likeCount
});
}
父组件操作子组件的基本流程如下。
- 父组件向子组件传入一个新参数
- 子组件在
componentWillReceiveProps方法里面处理新的参数,必要时调用setState方法 - 子组件在
componentWillUpdate方法里面处理新的state,但是一般来说,只使用componentWillReceiveProps方法就足够了
下面是一个父组件。
function rand(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
var ADayInTheLife = React.createClass({
getInitialState: function () {
return {
doing: "isCoding"
};
},
handleButtonClick: function () {
var activities = ["isCoding", "isEating", "isSleeping"];
var randAct = rand(activities);
this.setState({
doing: randAct
});
},
render: function () {
return (
<div>
<button onClick={this.handleButtonClick}>Next Activity</button>
<Jake activity={this.state.doing} />
</div>
);
}
});
React.render(<ADayInTheLife />, document.body);
上面代码中,父组件ADayInTheLife会更新子组件Jake。
下面是子组件Jake的代码。
var Jake = React.createClass({
getDefaultProps: function () {
return {
activity: "nothingYet"
};
},
getInitialState: function () {
return {
thinking: "nothing yet"
};
},
calcThinking: function (activity) {
var thoughts = {
isCoding: "yay, code",
isEating: "yum, code",
isSleeping: "where's the code?"
};
this.setState({
thinking: thoughts[activity]
})
},
componentWillReceiveProps: function (nextProps) {
this.calcThinking(nextProps.activity);
},
render: function () {
return <div>Jake: <b>{this.props.activity}</b> and thinking "{this.state.thinking}".</div>;
}
});
上面代码中,每次父组件要求Jake重新渲染,componentWillReceiveProps方法就会被调用。它执行calcThinking方法,再执行setState方法,使得Jake根据新的state完成渲染。
componentWillMount
componentWillMount方法在第一次渲染之前调用。它只会执行一次,在浏览器和服务器都会执行。一般用来对props和state进行初始化处理。
getInitialState: function() {
return {
items: this.props.initialItems || [],
sort: this.props.config.sort || { column: "", order: "" },
columns: this.props.config.columns
};
},
componentWillMount: function() {
this.loadData(this.props.dataSource);
},
loadData: function(dataSource) {
if (!dataSource) return;
$.get(dataSource).done(function(data) {
console.log("Received data");
this.setState({items: data});
}.bind(this)).fail(function(error, a, b) {
console.log("Error loading JSON");
});
},
上面代码中,getInitialState为首次渲染设置默认参数。在首次渲染之前,会执行componentWillMount方法。该方法内部调用loadData方法,发出AJAX请求。这个请求有可能成功,也有可能不成功,而且不知道需要多久才能完成。在AJAX请求返回结果,执行setState方法设置新的state之前,该组件将以默认值渲染。所以,使用getInitialState方法设置默认参数的意义就在这里。
注意,该方法之中不能调用setState。
componentWillUnmount
componentWillUnmount方法在组件从 DOM 移除后调用。一种用途是清除componentDidMount方法中添加的定时器。