https://reactjs.org/docs/react-component.html
React的Component生命週期分為三個階段:Mounting, Updating 和 Unmounting。
Mount 顧名思義是要將 Component 給掛到 DOM 上面,所以說如果 DOM 裡面如果不存在這個 component,肯定會執行 Mount 的流程。
Updating 是指將 Component 進行更新,例如原本 DOM 上面已經存在某個 component,如果 setState 被觸發了,那麼會走 Updating 的流程。
Unmount 是指從 DOM 上面拿掉 component,所以說在 component 被 unmount 之後,如果想要再讓它出現,必須走 Mount 的流程。
官方圖片
下面將會介紹三個階段的生命週期,並且介紹每個階段的生命週期函數。
首先是 Mounting 階段,Mounting 階段的生命週期函數有以下幾個:
下面將會介紹這些函數的用途。
constructor 是一個特殊的函數,它會在 component 建立的時候被呼叫,而且只會被呼叫一次。
constructor 的主要用途是用來初始化 state,或者是綁定函數的 this。
如下面的程式碼:
class MyComponent extends React.Component {
constructor(props) {
super(props);
// state 可以用 props 來初始化,用來儲存一些 component 的狀態
this.state = {counter: 0};
}
}
這個例子中,constructor 會將 state 初始化為 {counter: 0}。
static getDerivedStateFromProps 是一個靜態函數,它會在 component 建立的時候被呼叫,並且在每次更新的時候也會被呼叫。
static getDerivedStateFromProps 的主要用途是用來更新 state,它會在 render 之前被呼叫,並且在初始 render 和每次更新之前都會被呼叫。
它會接收兩個參數,第一個參數是 props,第二個參數是 state,並且必須回傳一個物件,這個物件會被合併到 state 中。
如下面的程式碼:
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
// ...
}
}
這個例子中,static getDerivedStateFromProps 會將 props 的值合併到 state 中。
render 是一個必須要實作的函數,它會在 component 建立的時候被呼叫,並且在每次更新的時候也會被呼叫。
render 的主要用途是用來渲染 component,它會回傳一個 React element,這個 element 會被用來更新 DOM。
如下面的程式碼:
class MyComponent extends React.Component {
render() {
return <div>Hello, World!</div>;
}
}
這個例子中,render 會回傳一個 div element,裡面的文字是 Hello, World!。
componentDidMount 是一個函數,它會在 component 建立的時候被呼叫,並且只會被呼叫一次。
componentDidMount 的主要用途是用來執行一些副作用,例如:發送 AJAX 請求、訂閱事件、設定計時器等等。
如下面的程式碼:
class MyComponent extends React.Component {
componentDidMount() {
// network request/ 真實DOM 操作/ 啟動計時器
}
render() {
return <div>Hello, World!</div>;
}
}
下面的程式碼是一個 Class Component,它會在建立的時候呼叫 constructor、static getDerivedStateFromProps、render、componentDidMount 這四個函數。
而且,每次按下按鈕的時候,都會呼叫 static getDerivedStateFromProps、render 這兩個函數。
你可以打開 console 觀察一下,看看這些函數是什麼時候被呼叫的。
點集button來把ClassComponentMounting渲染到DOM上
你可以複製下面的程式碼,自己試試看喔
import React from 'react';
type Props = {
title: string;
}
type State = {
count: number;
}
class ClassComponentMounting extends React.Component<Props, State> {
constructor(props: Props) {
console.log('constructor');
super(props);
this.state = {
count: 0,
}
}
static getDerivedStateFromProps(props: Props, state: State) {
console.log('getDerivedStateFromProps');
return null;
}
componentDidMount() {
console.log('componentDidMount');
}
render() {
console.log('render');
return (
<div>
<h1>{this.props.title}</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
handleClick = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}
}
export default ClassComponentMounting;
接下來是 Updating 階段,Updating 階段的生命週期函數有以下幾個:
下面將會介紹這些函數的用途。
static getDerivedStateFromProps 是一個靜態函數,它會在 component 建立的時候被呼叫,並且在每次更新的時候也會被呼叫。
static getDerivedStateFromProps 的主要用途是用來更新 state,它會在 render 之前被呼叫,並且在初始 render 和每次更新之前都會被呼叫。
它會接收兩個參數,第一個參數是 props,第二個參數是 state,並且需要回傳一個物件,這個物件會被合併到 state 中。
static getDerivedStateFromProps 會在 render 之前被呼叫,所以它不會造成 re-render,但是如果它回傳了一個物件,這個物件會被合併到 state 中,所以會造成 re-render。
如下面的程式碼:
static getDerivedStateFromProps(props: Props, state: State) {
console.log('getDerivedStateFromProps');
return null;
}
shouldComponentUpdate 會在每次更新的時候被呼叫,它會接收兩個參數,第一個參數是 nextProps,第二個參數是 nextState,並且需要回傳一個布林值,如果回傳 true,則會繼續更新,如果回傳 false,則不會繼續更新。
shouldComponentUpdate 會在 render 之前被呼叫,所以它不會造成 re-render,但是如果它回傳了 true,則會繼續更新,所以會造成 re-render。
如下面的程式碼:
shouldComponentUpdate(nextProps, nextState) {
console.log('componentShouldUpdate');
return true; // or false based on comparison of nextProps and this.props and/or nextState and this.state
}
render 會在每次更新的時候被呼叫,它會回傳一個 React 元素,這個 React 元素會被 render 到頁面上。
render 會在 shouldComponentUpdate 之後被呼叫,所以它不會造成 re-render,但是如果它回傳了一個 React 元素,這個 React 元素會被 render 到頁面上,所以會造成 re-render。
如下面的程式碼:
class MyComponent extends React.Component {
render() {
return <div>Hello, World!</div>;
}
}
getSnapshotBeforeUpdate 會在每次更新的時候被呼叫,它會接收兩個參數,第一個參數是 prevProps,第二個參數是 prevState,並且需要回傳一個物件,這個物件會被傳入 componentDidUpdate 中的 snapshot 參數。
getSnapshotBeforeUpdate 會在 render 之後被呼叫,所以它不會造成 re-render,但是如果它回傳了一個物件,這個物件會被傳入 componentDidUpdate 中的 snapshot 參數,所以會造成 re-render。
如下面的程式碼:
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate 會在每次更新的時候被呼叫,它會接收三個參數,第一個參數是 prevProps,第二個參數是 prevState,第三個參數是 snapshot。
componentDidUpdate 會在 getSnapshotBeforeUpdate 之後被呼叫,所以它不會造成 re-render。
如下面的程式碼:
componentDidUpdate() {
console.log('componentDidUpdate');
}
幫你整理一下,只有render階段會導致重新渲染
在 shouldComponentUpdate (以前叫做componentShouldUpdatte) 和 getSnapshotBeforeUpdate (以前叫做 componentWillUpdate) 中返回特定的值可能會觸發重新渲染
例如,在 shouldComponentUpdate 中返回 false 將會阻止組件的重新渲染,而在 getSnapshotBeforeUpdate 中返回非空值將會通知 React 執行更新操作。
shouldComponentUpdate(nextProps, nextState) {
return nextProps.id !== this.props.id;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return null;
}
下面的程式碼是一個 Class Component,它會在每次更新的時候呼叫 static getDerivedStateFromProps,並且在呼叫完 static getDerivedStateFromProps 之後,會將回傳的物件合併到 state 中,所以會造成 re-render。
當我們點擊按鈕的時候,會將 count 的值加一,然後將 count 的值設定到 state 中,所以會造成 re-render。
你可以打開 console 觀察一下,看看這些函數是什麼時候被呼叫的。
點擊button來把ClassComponentUpdating mount到DOM上
貼心提醒:如果你對觸發順序不清楚,可以回到最上面去看流程的圖片~
你可以複製下面的程式碼,自己試試看喔!
// 用來介紹 Class Component Updating 的頁面
import React from 'react';
type Props = {
title: string;
}
type State = {
count: number;
}
class ClassComponentUpdating extends React.Component<Props, State> {
constructor(props: Props) {
// console.log('constructor');
super(props);
this.state = {
count: 0,
}
}
static getDerivedStateFromProps(props: Props, state: State) {
console.log('getDerivedStateFromProps');
return null;
}
componentDidMount() {
// console.log('componentDidMount');
}
// 更新階段
// 由於React v16.3版本中 componentWillUpdate 已經被棄用 所以要改用別的寫法 如下面的 shouldComponentUpdate 和 getSnapshotBeforeUpdate
// componentShouldUpdatte() {
// console.log('componentShouldUpdatte');
// }
// componentWillUpdate() {
// console.log('componentWillUpdate');
// }
shouldComponentUpdate(nextProps, nextState) {
console.log('componentShouldUpdate');
return true; // or false based on comparison of nextProps and this.props and/or nextState and this.state
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
render() {
console.log('render');
return (
<div>
<h1>{this.props.title}</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
handleClick = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}
}
export default ClassComponentUpdating;
最後是 Unmounting 階段,Unmounting 階段的生命週期函數有以下幾個:
當一個元件被移除時,會觸發 componentWillUnmount
// 卸載階段
componentWillUnmount() {
console.log('componentWillUnmount');
}
點擊button來把ClassComponent渲染/收回到DOM上
// 用來介紹 Class Component Life Cycle 的頁面
import React from 'react';
type Props = {
title: string;
}
type State = {
count: number;
}
class ClassComponentLifeCycle extends React.Component<Props, State> {
// 建構階段
constructor(props: Props) {
console.log('constructor');
super(props);
this.state = {
count: 0,
}
}
static getDerivedStateFromProps(props: Props, state: State) {
console.log('getDerivedStateFromProps');
return null;
}
componentDidMount() {
console.log('componentDidMount');
}
// 更新階段
// 由於React v16.3版本中 componentWillUpdate 已經被棄用 所以要改用別的寫法 如下面的 shouldComponentUpdate 和 getSnapshotBeforeUpdate
// componentShouldUpdatte() {
// console.log('componentShouldUpdatte');
// }
// componentWillUpdate() {
// console.log('componentWillUpdate');
// }
shouldComponentUpdate(nextProps, nextState) {
console.log('componentShouldUpdate');
return true; // or false based on comparison of nextProps and this.props and/or nextState and this.state
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
// 卸載階段
componentWillUnmount() {
console.log('componentWillUnmount');
}
// 渲染階段
render() {
console.log('render');
return (
<div>
<h1>{this.props.title}</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
handleClick = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}
}
export default ClassComponentLifeCycle;
看完了Class Component的生命週期,接下來我們來看看Function Component的生命週期。
點我前往 學習 Function Component生命週期