React v16 生命週期函式詳解:如何、何時使用它們(React 元件生命週期的修訂和最新指南)

Yzz發表於2019-03-27

翻譯自 React 16 Lifecycle Methods: How and When to Use Them,作者:Scott Domes

參考 React.Component

概述:最近在學習 react v16.8.4,發現元件的生命週期發生了一些變化,原有的一些方法被廢棄,所以看了官方API介紹,再結合上述文章,做個總結。

publish:2019-03-27

自從我寫了關於 React 元件生命週期的第一篇文章以來,相關的鉤子函式、API已經發生了重大變化。 一些生命週期方法已被棄用,並引入了一些新的方法。 所以是時候進行更新了!

由於這次生命週期 API 有點複雜,我將這些方法分為四個部分:安裝(Mounting),更新(Updating),解除安裝(Unmounting)和錯誤(Error)。


Mounting - 元件的掛載

├── constructor()

├── static getDerivedStateFromProps

├── render

├── componentDidMount

constructor

如果您的元件是 Class Component,則呼叫的第一就是元件建構函式。 這不適用於 Functional Component。

相關建構函式可能是如下形式

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0,
    };
  }
}
複製程式碼

建構函式的引數為 props,你可以利用 super 來傳入

在建構函式中,你可以初始化 state、設定預設值。甚至你可以依據 props 來建立 state。

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: props.initialCounterValue,
    };
  }
}
複製程式碼

注意,現在建構函式是可選的,如果您的 Babel 設定支援 class fields,就可以像這樣初始化狀態:

class MyComponent extends Component {
  state = {
    counter: 0,
  };
}
複製程式碼

這種方法是被大家所提倡的。 你仍然可以根據 props 建立 state:

class MyComponent extends Component {
  state = {
    counter: this.props.initialCounterValue,
  };
}
複製程式碼

但是,如果需要使用 ref ,就仍需要建構函式。

class Grid extends Component {
  constructor(props) {
    super(props);
    this.state = {
      blocks: [],
    };
    // 在 constructor 中建立 ref
    this.grid = React.createRef();
  }
}
複製程式碼

我們需要建構函式來呼叫 createRef,以建立對 HTMLElement 元素的引用。還可以使用建構函式進行函式繫結,這也是可選的

class Grid extends Component {
  constructor(props) {
    super(props);
    this.state = {
      blocks: [],
    };
    // 1 
    this.handleChange.bind(this)
  }
  // 2 等同於 1
  handleChange = () => {}
}
複製程式碼

constructor總結: 建構函式的最常法:設定 state,建立 ref 和方法繫結。

getDerivedStateFromProps

掛載時,getDerivedStateFromProps 是渲染前呼叫的最後一個方法

一般使用 getDerivedStateFromProps 可以根據初始道具使用它來設定狀態。

static getDerivedStateFromProps(props, state) {
  return { blocks: createBlocks(props.numberOfBlocks) };
}
// log {blocks: Array(20)}
console.log(this.state);
複製程式碼

上述程式碼中依據 props.numberOfBlocks 來初始化期望的 state(函式return為狀態)。

注意: 我們可以將此程式碼放在 constructor 中,與之相 getDerivedStateFromProps的優點是它更直觀 - 它僅用於設定狀態,而建構函式有多種用途。

總結: getDerivedStateFromProps 的最常見用例(在mount期間):根據初始props返回狀態物件。

render

完成所有渲染的工作。它返回實際元件的 JSX,使用React時,將花費大部分時間在這裡。

渲染的最常見用例:返回 JSX 元件。

componentDidMount

在第一次渲染元件之後,觸發此方法。

如果需要載入資料,請在此處執行。 不要嘗試在 constructor 中載入資料或渲染,原因,react-interview-questions.

由於AJAX是非同步的,所以無法保證在元件掛載之前 AJAX 請求完成解析。 如果確實如此,那就意味著你要在未掛載的元件上嘗試 setState,這不僅不起作用,而且 React 報錯。 在componentDidMount中執行 AJAX 將保證有一個要更新的元件。

componentDidMount 觸發時,元件已完成第一 render,所以可以進行一些操作:

  • 在剛剛渲染的 <canvas> 元素上進行繪製
  • 訪問 DOM 節點
  • 新增事件偵聽器

基本上,在這裡你可以做所有依賴 DOM 的設定,並開始獲得你需要的所有資料,例如

componentDidMount() {
    // 利用 ref 訪問 dom 元素
    this.bricks = initializeGrid(this.grid.current);
    this.interval = setInterval(() => {
      // ajax 獲取資料
      this.addBlocks();
    }, 2000);
  }
}
複製程式碼

總結: 呼叫 AJAX 以載入元件的資料。

Updating - 元件的更新

├── static getDerivedStateFromProps

├── shouldComponentUpdate()

├── render

├── getSnapshotBeforeUpdate

├── componentDidUpdate()

getDerivedStateFromProps

是的,再來一次。 現在,它更有用了。

如果您需要根據 props 來更新 state,可以通過 return 新的狀態物件來完成該任務。

注,不建議依據 props 來處理 state,也就是說,只有逼不得已才使用該方法。 以下是一些例子:

  • 當 video 、audio 的 source 改變時,需要重置元素;
  • Server 資源更新後需要恢復 UI 元素;
  • 當內容改變時關閉相關元素。

即使有上述情況,通常也有更好的方法。 但是 getDerivedStateFromProps 可以情況變得更壞。

總結: getDerivedStateFromProps 一般用於 props 不足以支撐業務時,利用它來更新 state。

shouldComponentUpdate

典型的 React 教條,當一個元件收到新的 State 或 Props時,它應該更新。

但我們的元件有點困惑,它不確定是否要進行更新。

shouldComponentUpdate 方法的第一個引數為 nextProps,第二個引數為 nextState。shouldComponentUpdate 返回一個布林值,用於控制元件是否更新。

shouldComponentUpdate 賦予我們一項能力,只有在你關心的 props 改變時元件會才更新。

總結: 可以準確地控制元件重新渲染的時間,常用於優化 React,具體

render - 同上
getSnapshotBeforeUpdate

新新增的方法,觸發時刻在 render 之後,最新的渲染輸出提交給 DOM 之前。

呼叫渲染和最後顯示更改之間可能會有延遲, 如果你需要獲取 DOM 改變之前的一些資訊時,可以利用這個鉤子函式。

React v16 生命週期函式詳解:如何、何時使用它們(React 元件生命週期的修訂和最新指南)

從渲染到提交這個過程是非同步的,所以如果在 componentWillUpdate 訪問 DOM 資訊,在 componentDidUpdate 使用時,該資訊可能發生了修改,這一部分以官網的例子來說明

class ScrollingList extends React.Component {
  listRef = React.createRef();

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 如果在列表中新增新專案
    // 獲取列表的當前高度,以便我們稍後調整滾動
    if (prevProps.list.length < this.props.list.length) {
      return this.listRef.current.scrollHeight;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 如果我們 snapshot 的值不為空,說明新增了新專案
    // 調整滾動,以便這些新專案不會將舊專案推出可視區域
    if (snapshot !== null) {
      this.listRef.current.scrollTop +=
        this.listRef.current.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}
複製程式碼

總結: 檢視當前 DOM 的某些屬性,並將該值傳遞給componentDidUpdat。

componentDidUpdate

所有的改變都已經提交給 DOM。

componentDidUpdate 包含三個引數,之前的 props、state,以及 getSnapshotBeforeUpdate 的返回值,具體如上述例子。

總結: 對已經改變的 Dom 作出相關響應。

Unmounting

componentWillUnmount

元件快要結束了。

在元件登出之前,它會詢問您是否有任何最後一刻的請求。

您可以在此處取消任何傳出網路請求,或刪除與該元件關聯的所有事件偵聽器。

基本上,清理任何事情都只涉及有問題的元件 - 當它消失時,它應該完全消失。

總結: 清除事件監聽、定時器等,防止記憶體洩漏。

Errors

getDerivedStateFromError

發生了一些異常。

它能夠捕捉在他們的子元件樹中任意地方的 JavaScript 錯誤,記錄這些錯誤。當我們要展示一個 error 頁面時,可以利用它來完成。

static getDerivedStateFromError(error) {
  return { hasError: true };
}
複製程式碼

注意: 您必須返回更新的狀態物件。 不要將此方法用於任何副作用。 而是使用下面的 componentDidCatch。

總結: 依據錯誤資訊來修改元件的 state,同時展示出 error 頁面。

componentDidCatch

與上面非常相似,因為它在子元件中發生錯誤時被觸發。

與 getDerivedStateFromError 區別在於不是為了響應錯誤而更新狀態,可以執行任何副作用,例如記錄錯誤。

componentDidCatch(error, info) {
  sendErrorLog(error, info);
}
複製程式碼

注意: componentDidCatch 僅會捕獲渲染/生命週期方法中的錯誤。 如果在單擊處理程式中引發錯誤,則不會捕獲它。

通常只在特殊的地方使用錯誤邊界元件的 componentDidCatch。 這些元件包裝子樹的唯一目的是捕獲和記錄錯誤。

class ErrorBoundary extends Component {
  state = { errorMessage: null };
  static getDerivedStateFromError(error) {
    return { errorMessage: error.message };
  }
  componentDidCatch(error, info) {
    console.log(error, info);
  }
  render() {
    if (this.state.errorMessage) {
      return <h1>Oops! {this.state.errorMessage}</h1>;
    }
    return this.props.children;
  }
}
複製程式碼

總結: 捕獲、列印出錯誤資訊。

總結

以上是React v16的生命週期鉤子所適用的場景和具體使用方法。

相關文章