React 重新複習 class component 的生命週期

再次複習。

簡述

之前有寫過一篇:React class component 的生命週期,這篇就當作複習再練習一次。

這篇主要是參考 W3C 官方文件 來做的筆記,會挑 W3C 是因為它寫的比 React 官方文件 簡潔一點,但如果想知道更細節的話還是建議看 React 的官方文件會更完整。

正題

首先會分成三個階段:

  • Mounting
  • Updating
  • Ummounting

再貼一次這個好用的圖:

life-cycles

Mounting 階段

執行順序:

  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidmount

constructor

附註:只會執行一次

第一個會被先執行到的 function,會接收 props(來自 React.Component)這個參數,除此之外也會在這裡建立 state 的初始值。

順便做個補充,為了確保新建立的 Component 能夠正確運作,一定要記得先 super(props) 繼承所有 React.Component 的東西,

getDerivedStateFromProps

第二個會被執行的 function,會接受 props(從外面接收到的)和 state(在 constructor 建立的)。

這個階段可以根據 props 來更新或添加 state,向是這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class App extends React.Component {
constructor(props) {
console.log("1. constructor");
super(props);
// 原本是 red
this.state = { favoritecolor: "red" };
}

// 記得它是「static」方法,所以一定要加上前綴
// 會拿到這個元件的 props 跟 state
static getDerivedStateFromProps(props, state) {
console.log("2. getDerivedStateFromProps");
// 回傳的東西就會變成新的 state
return { favoritecolor: props.favcol };
}

render() {
console.log("3. render");
// 原本是 red,但會被 getDerivedStateFromProps 改成 yello
return <h1>My Favorite Color is {this.state.favoritecolor}</h1>;
}
}
// 傳一個 props 進去
ReactDOM.render(<App favcol="yello" />, document.getElementById("root"));

範例可以到 這裡 參考。

render

第三個會被執行的 function,這階段負責把 HTML 變成 DOM。

componentDidmount

附註:只會執行一次

附註:如果跟 getDerivedStateFromProps 改的是同一個 state,那最後會套用它的結果。

第四個會被執行的 function,在「把東西放到畫面上以後」才被執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class App extends React.Component {
constructor(props) {
console.log("1. constructor");
super(props);
// 原本是 red
this.state = { favoritecolor: "red" };
}

componentDidMount() {
console.log("3. componentDidMount");
// 一秒後改成綠色
setTimeout(() => {
this.setState({ favoritecolor: "green" });
}, 1000);
}

render() {
console.log("2. render");
return <h1>My Favorite Color is {this.state.favoritecolor}</h1>;
}
}

範例可以到 這裡 參考。

最後在強調一下,只有 rendergetDerivedStateFromProps 會在每次 re-render 的時候會被執行,其他兩個都只有一次,可以到 這裡 參考看看。

Update 階段

執行順序:

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

getDerivedStateFromProps

剛剛有說過 re-render 的時候這個會在執行一次,所以這邊它就是第一個會先被執行的 function。

shouldComponentUpdate

大概是最常看到的一個 function,總之它是一個蠻重要的 function,可以決定要不要 re-render,端看你給的回傳值是 true / false,另外會接收兩個參數是 nextPropsnextState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class App extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
}

shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
console.log("props", nextProps);
console.log("state", nextState);
return false;
}

render() {
return (
<div>
<div>回傳 false,所以不 re-render</div>
<div>Counter: {this.state.counter}</div>
<button
onClick={() => this.setState({ counter: this.state.counter + 1 })}
>
add
</button>
</div>
);
}
}
ReactDOM.render(<App ppp="yo" />, document.getElementById("root"));

範例一樣到 這邊 看。

render

render 每一次 re-render 都會被執行,所以在這個階段中當然也會。

這邊 render 要做的事情就是把更新 state 後的 HTML 反應 DOM 上面(注意是 DOM 不是畫面),這邊就不示範了。

getSnapshotBeforeUpdate

這個取名還真有意思,就是能在這邊拿到「更新前的 state」,所以乾脆來拍張照做紀念好了(?

這邊搭配 componentDidMount 來做個示範,原本的顏色是 red,但會改成 yellow,而 getSnapshotBeforeUpdate 會把更新前的 state 印到畫面上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class App extends React.Component {
constructor(props) {
super(props);
this.state = { favoriteColor: "red" };
}

componentDidMount() {
// 畫面 paint 完一秒後更新 state,觸發 re-render
setTimeout(() => {
this.setState({ favoriteColor: "yellow" });
}, 1000);
}

// W3C 是寫說要搭配 componentDidUpdate 來用,否則會出錯
// 但這邊在 codepen 上測試是 OK,所以先這樣寫
getSnapshotBeforeUpdate(prevProps, prevState) {
document.getElementById("div1").innerHTML = `
before update state, the favorite color was ${prevState.favoriteColor}
`;
}

render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoriteColor}</h1>
<div id="div1"></div>
</div>
);
}
}

ReactDOM.render(<App />, document.getElementById("root"));

不太懂的話就直接到這裡看 範例 吧。

componentDidUpdate

就是「更新完以後」會被執行的 function,這邊沿用剛剛 getSnapshotBeforeUpdate 的範例,看一下應該就知道在幹嘛了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class App extends React.Component {
constructor(props) {
super(props);
this.state = { favoriteColor: "red" };
}

componentDidMount() {
// 畫面 paint 完一秒後更新 state,觸發 re-render
setTimeout(() => {
this.setState({ favoriteColor: "yellow" });
}, 1000);
}

// 拿到更新前的值
getSnapshotBeforeUpdate(prevProps, prevState) {
document.getElementById("div1").innerHTML = `
before update state, the favorite color was ${prevState.favoriteColor}
`;
}

// 拿到更新後的值
// 因為是更新以後才被執行,所以 this.state 就會是新的 state
componentDidUpdate() {
document.getElementById("div2").innerHTML = `
after update state, the favorite color is ${this.state.favoriteColor}
`;
}

render() {
return (
<div>
<h1>My Favorite Color is {this.state.favoriteColor}</h1>
<div id="div1"></div>
<div id="div2"></div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

一樣附上 範例

Unmounting 階段

簡略來說應該只有這一個:componentWillUnmount

componentWillUnmount

就是在「元件從畫面上移除前」要做什麼?直接看 範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Header extends React.Component {
componentWillUnmount() {
alert("Before Header ummount");
}
render() {
return <h1>Hello I'm header</h1>;
}
}

class App extends React.Component {
constructor(props) {
super(props);
this.state = { show: true };
}

render() {
return (
<div>
{this.state.show && <Header />}
<button onClick={() => this.setState({ show: !this.state.show })}>
Toggle header
</button>
</div>
);
}
}

ReactDOM.render(<App />, document.getElementById("root"));
mentor-program-day128 mentor-program-day127
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×