Class Component 生命週期

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 的流程。

官方圖片

React Component Life Cycle

下面將會介紹三個階段的生命週期,並且介紹每個階段的生命週期函數。

詳細介紹

Mounting:React 將一個 DOM 元素插入到頁面上

首先是 Mounting 階段,Mounting 階段的生命週期函數有以下幾個:

  • constructor
  • static getDerivedStateFromProps
  • render
  • componentDidMount

下面將會介紹這些函數的用途。

constructor

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

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

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

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:將一個 DOM 元素更新到頁面上

接下來是 Updating 階段,Updating 階段的生命週期函數有以下幾個:

  • static getDerivedStateFromProps
  • shouldComponentUpdate (以前叫做componentShouldUpdatte)
  • render
  • getSnapshotBeforeUpdate (以前叫做componentWillUpdate)
  • componentDidUpdate

下面將會介紹這些函數的用途。

static getDerivedStateFromProps

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

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

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

getSnapshotBeforeUpdate 會在每次更新的時候被呼叫,它會接收兩個參數,第一個參數是 prevProps,第二個參數是 prevState,並且需要回傳一個物件,這個物件會被傳入 componentDidUpdate 中的 snapshot 參數。

getSnapshotBeforeUpdate 會在 render 之後被呼叫,所以它不會造成 re-render,但是如果它回傳了一個物件,這個物件會被傳入 componentDidUpdate 中的 snapshot 參數,所以會造成 re-render。

如下面的程式碼:

getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('getSnapshotBeforeUpdate');
    return null;
}

componentDidUpdate

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 階段,Unmounting 階段的生命週期函數有以下幾個:

  • componentWillUnmount

當一個元件被移除時,會觸發 componentWillUnmount

// 卸載階段
componentWillUnmount() {
    console.log('componentWillUnmount');
}

下面提供完整的流程與程式碼,讓你可以在控制台看到

點擊button來把ClassComponent渲染/收回到DOM上

完整的code


// 用來介紹 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;

前往Function Component

看完了Class Component的生命週期,接下來我們來看看Function Component的生命週期。

點我前往 學習 Function Component生命週期