React的核心是組件,組件在創建和渲染的過程中,需要調用固定的鉤子函數,也稱為組件的“生命周期”。利用生命周期函數,可以做初始化工作,并在渲染過程中實現一些特定功能。
1. 生命周期函數
組件的整個生命周期會涉及如下函數:
鉤子函數 | 說明 |
---|---|
getDefaultProps | 設置props默認配置 |
getInitialState | 設置state默認配置 |
componentWillMount | 組件被注入DOM之前被調用 |
render | 渲染組件時被調用 |
componentDidMount | 組件被注入DOM之后被調用 |
componentWillReceiveProps | 掛載的組件接收到新的props時被調用 |
shouldComponentUpdate | 指定是否更新props和state |
componentWillUpdate | 更新組件時,渲染之前被調用 |
componentDidUpdate | 更新組件時,渲染之后被調用 |
componentWillUnMount | 卸載組件 |
可以參考下圖(來自網絡)進一步了解整個流程。
react life cycle.jpg
這里特殊說明兩個方法:getDefaultProps
和getInitialState
。
- 用
React.createClass()
函數創建組件,調用的是這兩個鉤子函數。 - ES6類方法創建的組件,初始化
props
用的是靜態屬性defaultProps
;初始化state
是在構造函數constructor
里做的。
總結:
- props更改時,會依次調用
componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
; - state更改時,會依次調用
shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
;
小貼士
shouldComponentUpdate 是一個非常重要的鉤子函數,這個函數默認返回true。
在React中,調用setState方法,React不會立即對其更新,而是將其標記為“臟”狀態
(組件狀態更新不會立刻生效,React使用事件輪詢對變更內容進行批量繪制)。
當事件輪詢結束后,React將“臟”組件及其子節點進行重繪,所有后代節點的render方法都會被調用,哪怕它們沒法發生變化。
通過shouldComponentUpdate方法,可以阻止子樹的重繪
(自行實現該方法并返回false,React會跳過該組件及其子組件的重繪過程)。
--- 參考《Pro React》
下面,我們來看一個真實例子,觀察組件生命周期的變換(采用ES6類模式)。
2. 組件實例
class DangerButton extends React.Component {
/*類型檢查*/
static propTypes = {
onClick: React.PropTypes.func,
text: React.PropTypes.string
};
/*初始化props值*/
static defaultProps = {
type: 'btn'
};
constructor(props) {
super(props);
// 初始化state值
this.state = {count: 0};
this.increaseCount = this.increaseCount.bind(this);
}
increaseCount() {
this.setState({count: this.state.count + 1});
}
getObjectValues(obj){
var array = [];
for(let key in obj){
array.push(key + ":" + obj[key]);
}
return array.join(";");
}
/*----------------start: life cycle---------------*/
/*組件被注入DOM之前*/
componentWillMount() {
var button = document.getElementById('dangerBtn');
console.log("componentWillMount:" + button);
}
/*組件被注入DOM之后*/
componentDidMount() {
var button = document.getElementById('dangerBtn');
console.log("componentDidMount:" + button);
}
/*掛載的組件接收到新的props時被調用*/
componentWillReceiveProps(nextProps){
console.log("componentWillReceiveProps:" + nextProps);
}
/*指定是否更新props和state*/
shouldComponentUpdate(nextProps, nextState){
console.log("shouldComponentUpdate-true!");
return true;
}
/*更新組件時,渲染之前*/
componentWillUpdate(nextProps, nextState){
console.log("componentWillUpdate-nextProps:" + this.getObjectValues(nextProps));
console.log("componentWillUpdate-nextState:" + this.getObjectValues(nextState));
}
/*更新組件時,渲染之后*/
componentDidUpdate(prevProps, prevState){
console.log("componentDidUpdate-prevProps:" + this.getObjectValues(prevProps));
console.log("componentDidUpdate-prevState:" + this.getObjectValues(prevState));
}
/*卸載組件*/
componentWillUnMount() {
var button = document.getElementById('dangerBtn');
console.log("componentDidMount:" + button);
}
render() {
console.log("rendering....");
return (<div>
<button id="dangerBtn" className='red' onClick={this.increaseCount}>
<span className='white'>{this.props.text}</span>
</button>
<p>Click count: {this.state.count}</p>
</div>);
}
/*----------------end: life cycle---------------*/
}
ReactDOM.render(
<DangerButton text="click it!"/>,
document.getElementById('container')
);
第一次渲染DangerButton組件時,控制臺打印如下信息:
componentWillMount:null
rendering....
componentDidMount:[object HTMLButtonElement]
可見,渲染組件的componentWillMount
階段,真實DOM還沒有生成;到了componentDidMount
階段,組件才真正被加載到DOM中。
然后,點擊DangerButton,count
值加一,控制臺打印如下信息:
shouldComponentUpdate-true!
componentWillUpdate-nextProps:text:click it!;type:btn
componentWillUpdate-nextState:count:1
rendering....
componentDidUpdate-prevProps:text:click it!;type:btn
componentDidUpdate-prevState:count:0
可見,如果組件自身的state更新后(點擊button,觸發onClick事件),會依次執行shouldComponentUpdate
,componentWillUpdate
,render
和componentDidUpdate
函數。
小結
在組件整個生命周期中,涉及到兩種變量來傳遞/存儲值,prop
和state
。那么,它們的使用場景是什么?有什么區別呢?下一節,我們將繼續探索......