Reactv16.8.6生命週期函式

G_Owen發表於2019-07-31

元件生命週期函式

React 主動呼叫的方法,也可重寫這些方法

生命週期圖譜

當元件例項被建立並插入 DOM 中時,其生命週期呼叫順序如下:

constructor(props)

如果不需要初始化 state 或 不進行方法繫結,則不需要使用該方法

在元件掛載之前會先呼叫該方法,在實現建構函式時必須先呼叫super(props)方法,否則會出現BUG
通常,建構函式僅用於兩種情況:1. 初始化 state 2. 為事件處理函式繫結例項
在該方法中不要使用 setState() 方法,在其他方法中使用setState()改變 state
為什麼 props 複製給 state 會產生 bug

constructor(props) {
  super(props);
  // 不要在這裡呼叫 this.setState()
  this.state = {
    counter: 0,
    name:props.name // 嚴禁這樣賦值,props.name值更新時 state.name並不會更新
   };
  this.handleClick = this.handleClick.bind(this);
}

static getDerivedStateFromProps() (此方法不常用)

此方法會在 render 方法之前呼叫,並且初始化和資料更新時都會呼叫,它返回一個物件更新 state,如果返回null 則不更新任何內容。

此方法適用於 state 值在任何時候都取決於props 的情況。

render()

render 是 class 元件必須實現的方法

當該方法被呼叫時,它會監測 props 和 state 的變化,並且返回以下型別之一:

  • React 元素:通過JSX建立,渲染成對應的DOM節點或自定義元件
  • 陣列或fragments: 使render方法可以返回多個元素 frgments
  • Portals:可以渲染子節點到不同的DOM子樹匯中portals
  • 字串或數值型別: 在DOM中會被渲染為文字節點、
  • Boolean 或 null:什麼都不渲染

render方法最好為純函式,即在不修改元件 state情況下,每次呼叫時都返回相同的結果,並且不會直接與瀏覽器互動

如果要和瀏覽器互動,可以在其他生命週期函式中執行,注意:shoouldComponentUpdate方法中返回false,將不會呼叫render方法

class Example extemds React.Component{
shouldComponentUpdate(nextProps, nextState){
  return false
}
render(){ // 不會執行
  <div>owen</div>
  }
}

componentDIdMount()

此方法會在元件掛載後(插入DOM樹中)呼叫,初始化的資料的好地方

元件的 propsstate 發生變化會觸發更新。元件更新的生命週期呼叫順序如下:

static getDerivedStateFromProps() (此方法不常用)(已解釋)

shouldComponentUpdate(nextProps, nextState) (此方法不常用)

當state 或 props 變化時該方法會在渲染執行前呼叫預設返回值為true,首次載入不會被呼叫

根據該方法的返回值判斷元件輸出是否受當前 state 或 props 更改的影響。預設為 state 每次更新重新渲染

此方法進僅做為效能優化的方式存在,不要企圖依靠此方法來“阻止”渲染,因為這可能會產生 bug。你應該考慮使用內建的 PureComponent 元件,而不是手動編寫 shouldComponentUpdate()。PureComponent 會對 props 和 state 進行淺層比較,並減少了跳過必要更新的可能性。

render()(已解釋)

getSnapshotBeforeUpdate() (此方法不常用)

此方法在最近一次渲染輸出(提交到DOM節點)之前呼叫。使元件能在傳送更改前從DOM中捕獲一些資訊(如 位置)。此生命週期的返回值將作為引數傳遞給 componentDidUpdate()

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 我們是否在 list 中新增新的 items ?
    // 捕獲滾動​​位置以便我們稍後調整滾動位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我們 snapshot 有值,說明我們剛剛新增了新的 items,
    // 調整滾動位置使得這些新 items 不會將舊的 items 推出檢視。
    //(這裡的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

上述示例中,重點是從 getSnapshotBeforeUpdate 讀取 scrollHeight 屬性,因為 “render” 階段生命週期(如 render)和 “commit” 階段生命週期(如 getSnapshotBeforeUpdate 和 componentDidUpdate)之間可能存在延遲。

componentDidUpdate(prevProps, prevState, snapshot)

此方法會在資料更新後立即呼叫,首次載入不會被呼叫,在此方法中使用 setState必須將它放到條件語句中,否則會導致死迴圈。還會導致額外的重新渲染,影響效能

componentDidUpdate(prevProps) {
  // 典型用法(不要忘記比較 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

注意:如果 shouldComponentUpdate() 返回值為 false,則不會呼叫 componentDidUpdate()。

當元件從 DOM 中移除時會呼叫如下方法:

componentWillUnmount()

此方法會在元件解除安裝銷燬前呼叫,可以執行必要的清理操作,如 定時器,取消網路請求,或清除componentDidMount() 中建立的訂閱等。

當渲染過程,生命週期,或子元件的建構函式中丟擲錯誤時,會呼叫如下方法:

Error boundaries:捕獲渲染期間及整個樹的函式傳送的錯誤,渲染降級 UI,但自身的錯誤無法捕獲 React 16中的錯誤處理

static getDerivedStateFromError(error) (此方法不常用)

次生命週期會在後代元件丟擲錯誤後呼叫,將錯誤作為引數,返回一個值更新state,在渲染期間不允許出現副作用,建議使用 componentDidCatch()

componentDidCatch(error, info) (此方法不常用)

此方法在後代元件丟擲錯誤後被呼叫

如果發生錯誤,可以通過呼叫 setState 使用 componentDidCatch() 渲染降級 UI,但在未來的版本中將不推薦這樣做。 可以使用靜態 getDerivedStateFromError() 來處理降級渲染。

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染可以顯降級 UI
    return { hasError: true };
  }
  componentDidCatch(error, info) {
    // "元件堆疊" 例子:
    //   in ComponentThatThrows (created by App)
    //   in ErrorBoundary (created by App)
    //   in div (created by App)
    //   in App
    logComponentStackToMyService(info.componentStack);
  }
  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定義的降級  UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

Example

class Square extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      value:null
    };
  }
  static getDerivedStateFromProps(props, state) {
    // 例項化元件之後以及在重新呈現元件之前呼叫新的靜態生命週期。它可以返回要更新的物件state,或null指示新物件props不需要任何state更新。
  }
   componentDidMount() { // 元件被渲染到 DOM 中後執行
    console.log('DidMount: 1')
  }

  shouldComponentUpdate(){
    // 更新前
  }

  getSnapshotBeforeUpdate(){
    //
  }
  componentDidUpdate() {
    // 更新後
  }

  static getDerivedStateFromError() {
      // 出錯時
  }
  componentDidCatch(){
    // capture error
  }
  compoentwillUnmount(){ // 元件被刪除的時候
    console.log('UnMount: end')

  }
  render() {
    return (
      <button className="square" onClick = {()=>{this.setState({value:'X'})}
      }>
        {this.state.value}
      </button>
    );
  }
}

Owen 的個人部落格

參考資料:React官網

相關文章