學習React有一個很重要的概念需要弄清楚,那就是React元件的生命週期,以及它跟 setState 所引起的 React生命週期鉤子的調起情況。React 生命週期分為掛載階段、更新階段和解除安裝階段。下面我將使用 create-react-app
做一個簡單的分析,React版本是16.8.1
。
一、掛載階段
1.1 元件初次掛載,依序執行以下方法。除了 render,其他3個函式只會執行一次。
- constructor
- componentWillMount
- render
- componentDidMount
核心程式碼如下,簡單輸出各生命週期鉤子的日誌資訊:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
count:0
};
console.log('constructor')
}
componentWillMount(){
console.log('componentWillMount')
}
componentDidMount() {
console.log('componentDidMount')
}
//WARNING! To be deprecated in React v17. Use componentDidUpdate instead.
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(){
console.log('componentDidUpdate')
}
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps')
}
render() {
console.log('render', this.state)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<button onClick={this.handleClick}>click me</button>
</header>
</div>
);
}
}
export default App;
複製程式碼
瀏覽器輸出日誌如下,結果很簡單明瞭:
1.2 接下來,要稍微搞一下事情了,在componentWillMount
中setState更新狀態會怎麼樣呢?
componentWillMount(){
console.log('componentWillMount')
this.setState({foo: 'bar'});
}
複製程式碼
瀏覽器輸出日誌如下,可以看到在componentWillMount
中更新狀態不會觸發更新過程,但新的狀態會及時在 render 中體現:
在這裡,我們需要注意到 setState 是非同步的操作:
componentWillMount(){
console.log('componentWillMount')
console.log('componentWillMount before setState',this.state)
this.setState({foo: 'bar'});
console.log('componentWillMount after setState',this.state)
}
複製程式碼
瀏覽器輸出日誌如下,可以看到執行setState操作後,state 物件沒有立即生效,而是到了 render 函式中才生效。
1.3 利用 setState 的回撥,可以獲知渲染完畢後的新的 state 內容,程式碼和日誌資訊如下:
componentWillMount(){
console.log('componentWillMount')
console.log('componentWillMount before setState',this.state)
this.setState({foo: 'bar'},() => {
console.log('componentWillMount after setState',this.state)
});
}
複製程式碼
1.4 如果連續多次 setState,react 內部做了優化,不會觸發多次更新。程式碼和日誌資訊如下:
componentWillMount(){
console.log('componentWillMount')
this.setState({foo: 'bar'});
this.setState({foo: 'bar2'});
}
複製程式碼
1.5 如果需要獲知當前最新的 state 內容,以計算下一個 state 內容,則需要使用函式傳參形式的 setState,見下圖。
可以在此看處 setState後於console.log('do some calculate or other operation...')
執行,再次反映了 setState的非同步執行機制。這裡,透露了一個最佳實踐建議,即:
在 setState 之前做好計算,最後再一次性 setState。這樣,程式碼順序就和執行順序一致,出問題時才不會一臉懵逼。
1.6 然後是還沒怎麼提到的componentDidMount
元件掛載完成後執行,可以在這裡拉取伺服器資料(當然,也可以在上面提到的componentWillMount
中拉取遠端資料)。也可以在這裡新增對dom 的監聽及其他對dom 的操作,還可以建立定時器。(相應的,需要在componentDidMount
中銷燬 dom 的監聽和 timer,以免造成記憶體洩漏。如果程式碼更嚴謹一點,可同時在 componentWillMount 和 componentDidMount 中銷燬 dom 的監聽和 timer,以免某些情況下 componentDidMount 沒有執行。)