React生命周期


React生命周期

React的生命周期從廣義上分為掛載、渲染、卸載三個階段,在React的整個生命周期中提供很多鈎子函數在生命周期的不同時刻調用。

描述

此處描述的是使用class類組件提供的生命周期函數,每個組件都包含自己的生命周期方法,通過重寫這些方法,可以在運行過程中特定的階段執行這些方法,常用的生命周期有constructor()render()componentDidMount()componentDidUpdate()componentWillUnmount()

掛載過程

當組件實例被創建並插入DOM中時,其生命周期調用順序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

在這個階段的componentWillMount()生命周期即將過時,在新代碼中應該避免使用。

更新過程

當組件的propsstate發生變化時會觸發更新,組件更新的生命周期調用順序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

在這個階段的componentWillUpdate()componentWillReceiveProps()生命周期即將過時,在新代碼中應該避免使用。

卸載過程

當組件從DOM中移除時,組件更新的生命周期調用順序如下:

  • componentWillUnmount()

錯誤處理

當渲染過程,生命周期,或子組件的構造函數中拋出錯誤時,會調用如下方法:

  • static getDerivedStateFromError()
  • componentDidCatch()

生命周期

constructor()

React組件掛載之前,會調用它的構造函數,如果不初始化state或不進行方法綁定,則不需要為React組件實現構造函數。在為React.Component子類實現構造函數時,應在其他語句之前前調用super(props),否則this.props在構造函數中可能會出現未定義的錯誤。
通常在React中構造函數僅用於以下兩種情況:

  • 通過給this.state賦值對象來初始化內部state
  • 為事件處理函數綁定實例。
constructor(props) {
    super(props);
}

static getDerivedStateFromProps()

getDerivedStateFromProps靜態方法會在調用render方法之前調用,並且在初始掛載及后續更新時都會被調用,它應返回一個對象來更新state,如果返回null則不更新任何內容。此方法無權訪問組件實例,如果確實需要,可以通過提取組件props的純函數及class之外的狀態,在getDerivedStateFromProps()和其他class方法之間重用代碼。此外,不管原因是什么,都會在每次渲染前觸發此方法。

static getDerivedStateFromProps(props, state) {}

render()

render()方法是class組件中唯一必須實現的方法,render()函數應該為純函數,這意味着在不修改組件state的情況下,每次調用時都返回相同的結果,並且它不會直接與瀏覽器交互。如需與瀏覽器進行交互,請在componentDidMount()或其他生命周期方法中執行操作,保持render()為純函數。當render被調用時,它會檢查this.propsthis.state的變化並返回以下類型之一:

  • React元素,通常通過JSX創建,例如<div />會被React渲染為DOM節點,<MyComponent />會被React渲染為自定義組件,無論是<div />還是<MyComponent />均為React元素。
  • 數組或fragments,使得render方法可以返回多個元素。
  • Portals,可以渲染子節點到不同的DOM子樹中。
  • 字符串或數值類型,它們在DOM中會被渲染為文本節點。
  • 布爾類型或null,什么都不渲染,主要用於支持返回test && <Child />的模式,其中test為布爾類型。
render() {}

componentDidMount()

componentDidMount()會在組件掛載后(即插入DOM樹后)立即調用,依賴於DOM節點的初始化應該放在這里,如需通過網絡請求獲取數據,此處是實例化請求的好地方。這個方法是比較適合添加訂閱的地方,如果添加了訂閱,請不要忘記在componentWillUnmount()里取消訂閱。
你可以在componentDidMount()里直接調用setState(),它將觸發額外渲染,但此渲染會發生在瀏覽器更新屏幕之前,如此保證了即使在render()兩次調用的情況下,用戶也不會看到中間狀態,請謹慎使用該模式,因為它會導致性能問題。通常應該在constructor()中初始化state,如果你的渲染依賴於DOM節點的大小或位置,比如實現modalstooltips等情況下,你可以使用此方式處理。

componentDidMount() {}

shouldComponentUpdate()

propsstate發生變化時,shouldComponentUpdate()會在渲染執行之前被調用,返回值默認為true,首次渲染或使用forceUpdate()時不會調用該方法。根據shouldComponentUpdate()的返回值,判斷React組件的輸出是否受當前stateprops更改的影響。默認行為是state每次發生變化組件都會重新渲染,大部分情況下,你應該遵循默認行為。
此方法僅作為性能優化的方式而存在,不要企圖依靠此方法來阻止渲染,因為這可能會產生bug,你應該考慮使用內置的PureComponent組件,而不是手動編寫shouldComponentUpdate()PureComponent會對propsstate進行淺層比較,並減少了跳過必要更新的可能性。
如果你一定要手動編寫此函數,可以將this.propsnextProps以及this.statenextState進行比較,並返回false以告知React可以跳過更新。請注意,返回false並不會阻止子組件在state更改時重新渲染。不建議在shouldComponentUpdate()中進行深層比較或使用JSON.stringify(),這樣非常影響效率,且會損害性能。目前如果shouldComponentUpdate()返回false,則不會調用UNSAFE_componentWillUpdate()render()componentDidUpdate()。后續版本React可能會將shouldComponentUpdate視為提示而不是嚴格的指令,並且當返回false時仍可能導致組件重新渲染。

shouldComponentUpdate(nextProps, nextState) {}

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate()在最近一次渲染輸出(提交到DOM節點)之前調用,它使得組件能在發生更改之前從DOM中捕獲一些信息(例如滾動位置),此生命周期的任何返回值將作為參數傳遞給componentDidUpdate(),該方法應返回snapshot的值或null
此用法並不常見,但它可能出現在UI處理中,如需要以特殊方式處理滾動位置的聊天線程等。

getSnapshotBeforeUpdate(prevProps, prevState) {}

componentDidUpdate()

componentDidUpdate()會在更新后會被立即調用,首次渲染不會執行此方法。當組件更新后,可以在此處對DOM進行操作,如果你對更新前后的props進行了比較,也可以選擇在此處進行網絡請求(例如,當props未發生變化時,則不會執行網絡請求。如果shouldComponentUpdate()返回值為false,則不會調用componentDidUpdate()
你也可以在componentDidUpdate()中直接調用setState(),但請注意它必須被包裹在一個條件語句里,否則會導致死循環,因為他將無限次觸發componentDidUpdate()。它還會導致額外的重新渲染,雖然用戶不可見,但會影響組件性能。
如果組件實現了getSnapshotBeforeUpdate()生命周期(不常用),則它的返回值將作為componentDidUpdate()的第三個參數snapshot參數傳遞,否則此參數將為undefined

componentDidUpdate(prevProps, prevState, snapshot) {}

componentWillUnmount()

componentWillUnmount()會在組件卸載及銷毀之前直接調用,在此方法中執行必要的清理操作,例如清除timer、取消網絡請求或清除在componentDidMount()中創建的訂閱等。
componentWillUnmount()中不應調用setState(),因為該組件將永遠不會重新渲染,組件實例卸載后,將永遠不會再掛載它。

componentWillUnmount() {}

static getDerivedStateFromError()

此生命周期會在后代組件拋出錯誤后被調用,它將拋出的錯誤作為參數,並返回一個值以更新stategetDerivedStateFromError()會在渲染階段調用,因此不允許出現副作用,如遇此類情況,請改用componentDidCatch()

static getDerivedStateFromError(error) {}

componentDidCatch()

此生命周期在后代組件拋出錯誤后被調用,componentDidCatch()會在提交階段被調用,因此允許執行副作用,它應該用於記錄錯誤之類的情況它接收兩個參數:

  • error: 拋出的錯誤。
  • info: 帶有componentStack key的對象,其中包含有關組件引發錯誤的棧信息。
componentDidCatch(error, info) {}

示例

React組件的常用生命周期示例。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>React生命周期</title>
</head>

<body>
  <div id="root"></div>
</body>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">

  class Clock extends React.Component {
    constructor(props) {
      super(props);
      this.state = { date: new Date() };
    }
    componentDidMount() {
      console.log("ComponentDidMount", this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
      this.timer = setInterval(() => this.tick(), 1000);
    }
    componentWillUnmount() {
      console.log("ComponentWillUnmount", this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
      clearInterval(this.timer);
    }
    tick() {
      this.setState({ date: new Date() });
    }
    render() {
      return (
        <div>
          <h1>{this.props.tips}</h1>
          <h2>Now: {this.state.date.toLocaleTimeString()}</h2>
        </div>
      );
    }
  }

  class App extends React.Component{
    constructor(props){
      super(props);
      this.state = { 
        showClock: true,
        tips: "Hello World!"
      }
    }
    componentDidUpdate(prevProps, prevState) {
      console.log("ComponentDidUpdate", this);
      console.log(this.props);
      console.log(this.state);
      console.log("");
    }
    updateTips() {
      this.setState((state, props) => ({
        tips: "React update"
      }));
    }
    changeDisplayClock() {
      this.setState((state, props) => ({
        showClock: !this.state.showClock
      }));
    }
    render() {
      return (
        <div>
          {this.state.showClock && <Clock tips={this.state.tips} />}
          <button onClick={() => this.updateTips()}>更新tips</button>
          <button onClick={() => this.changeDisplayClock()}>改變顯隱</button>
        </div>
      );
    }
  }

  var vm = ReactDOM.render(
    <App />,
    document.getElementById("root")
  );
</script>

</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://www.jianshu.com/p/b331d0e4b398
https://www.cnblogs.com/soyxiaobi/p/9559117.html
https://zh-hans.reactjs.org/docs/react-component.html
https://zh-hans.reactjs.org/docs/state-and-lifecycle.html
https://www.runoob.com/react/react-component-life-cycle.html
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM