關於React v16.3 新生命週期

變動的部分

react v16.3終於出來了,最大的變更莫過於生命週期去掉了如下三個html

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

同時爲了彌補失去上面三個週期的不足又加了兩個前端

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

固然,這個更替是緩慢的,在整個16版本里都能無障礙的使用舊的三生命週期,但值得注意的是,舊的生命週期(unsafe)不能和新的生命週期同時出如今一個組件,不然會報錯「你使用了一個不安全的生命週期」。react

爲何要改

舊的生命週期十分完整,基本能夠捕捉到組件更新的每個state/props/ref,沒有什從邏輯上的毛病。安全

可是架不住官方本身搞事情,react打算在17版本推出新的Async Rendering,提出一種可被打斷的生命週期,而能夠被打斷的階段正是實際dom掛載以前的虛擬dom構建階段,也就是要被去掉的三個生命週期。bash

生命週期一旦被打斷,下次恢復的時候又會再跑一次以前的生命週期,所以componentWillMount,componentWillReceiveProps, componentWillUpdate都不能保證只在掛載/拿到props/狀態變化的時候刷新一次了,因此這三個方法被標記爲不安全。markdown

兩個新生命週期

static getDerivedStateFromProps

  • 觸發時間(v16.4修正):組件每次被rerender的時候,包括在組件構建以後(render以前最後執行),每次獲取新的props或state以後。在v16.3版本時,組件state的更新不會觸發該生命週期。
  • 每次接收新的props以後都會返回一個對象做爲新的state,返回null則說明不須要更新state.
  • 配合componentDidUpdate,能夠覆蓋componentWillReceiveProps的全部用法
class Example extends React.Component {
  static getDerivedStateFromProps(nextProps, prevState) {
    // 沒錯,這是一個static
  }
}
複製代碼

getSnapshotBeforeUpdate

  • 觸發時間: update發生的時候,在render以後,在組件dom渲染以前。
  • 返回一個值,做爲componentDidUpdate的第三個參數。
  • 配合componentDidUpdate, 能夠覆蓋componentWillUpdate的全部用法。
class Example extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
  }
}
複製代碼

建議用法總結

  1. 初始化state — Initializing statedom

    • 在constructor初始化state就能夠了
  2. 請求數據 — Fetching external data異步

    • 在componentDidMount請求異步加載的數據
    • 有一種錯覺,在componentWillMount請求的數據在render就能拿到,但其實render在willMount以後幾乎是立刻就被調用,根本等不到數據回來,一樣須要render一次「加載中」的空數據狀態,因此在didMount去取數據幾乎不會產生影響。
  3. 添加事件監聽 — Adding event listeners (or subscriptions)async

    • 在componentDidMount中添加加事件監聽
    • react只能保證componentDidMount-componentWillUnmount成對出現,componentWillMount能夠被打斷或調用屢次,所以沒法保證事件監聽能在unmount的時候被成功卸載,可能會引發內存泄露
  4. 根據props更新state — Updating state based on propside

    • 用getDerivedStateFromProps(nextProps, prevState), 將傳入的props更新到state上
    • 用來代替componentWillReceiveProps(nextProps, nextState),willReceiveProps常常被誤用,致使了一些問題,所以該方法將不被推薦使用。
    • getDerivedStateFromProps是一個static方法,意味着拿不到實例的this,因此想要在setState以前比對一下props有沒有更新,下面方法是不能用了
    if (this.props.currentRow !== nextProps.currentRow) {
     	...
     }
    複製代碼

    取而代之的是,額外寫一個state來記錄上一個props (` ^ ‘)

    if (nextProps.currentRow !== prevState.lastRow) {
      return {
        ...
        lastRow: nextProps.currentRow,
      };
      // 不更新state
      return null
    }
    複製代碼
    • 爲何咱們不給一個prevProps參數呢,官方解釋是,一來prevProps第一次被調用的時候是null,每次更新都要判斷耗性能,二來若是你們都習慣了,之後react不記錄prevProps的話(啥),能夠省下很多內存
  5. 觸發請求 — Invoking external callbacks

    • 在生命週期中因爲state的變化觸發請求,在componentDidUpdate中進行
    • 爲何不在componentWillUpdate中的理由同上2
  6. props更新引發的反作用 — Side effects on props change

    • props更改引起的可視變化(反作用,好比log,ga),在componentDidUpdate中處理
    // 在didUpdate中根據props更新的確很不適應
    // props變了也是能夠觸發update的
    componentDidUpdate(prevProps, prevState) {
    	if (this.props.isVisible !== prevProps.isVisible) {
    	  logVisibleChange(this.props.isVisible);
    	}
    }
    複製代碼
    • componentWillUpdate, componentWillReceiveProps在一次更新中可能會被觸發屢次,所以這種只但願觸發一次的反作用應該放在保證只觸發一次的componentDidUpdate中。
  7. props更新時從新請求 — Fetching external data when props change

    • 傳入新的props時從新異步取數據,getDerivedStateFromProps+ componentDidUpdate 替代 componentWillReceiveProps
    // old
      componentWillReceiveProps(nextProps) {
        if (nextProps.id !== this.props.id) {
        	this.setState({externalData: null});
          this._loadAsyncData(nextProps.id);
        }
      }
    複製代碼
    // new
      static getDerivedStateFromProps(nextProps, prevState) {
        // Store prevId in state so we can compare when props change.
        if (nextProps.id !== prevState.prevId) {
          return {
            externalData: null,
            prevId: nextProps.id,
          };
        }
        // No state update necessary
        return null;
      }
      componentDidUpdate(prevProps, prevState) {
        if (this.state.externalData === null) {
          this._loadAsyncData(this.props.id);
        }
      }
    複製代碼
  8. 在更新前記錄原來的dom節點屬性 — Reading DOM properties before an update

    • 在upate以前獲取dom節點,getSnapshotBeforeUpdate(prevProps, prevState)代替componentWillUpdate(nextProps, nextState)
    • getSnapshotBeforeUpdate在render以後,但在節點掛載前
    • componentDidUpdate(prevProps, prevState, snapshot)直接得到getSnapshotBeforeUpdate返回的dom屬性值

生命週期功能替換一覽

static getDerivedStateFromProps(nextProps, prevState) {
    4. Updating state based on props
    7. Fetching external data when props change // Clear out previously-loaded data so we dont render stale stuff
  }
  constructor() {
	1. Initializing state
  }
  componentWillMount() {
  	// 1. Initializing state
  	// 2. Fetching external data
  	// 3. Adding event listeners (or subscriptions)
  }
  componentDidMount() {
	2. Fetching external data
	3. Adding event listeners (or subscriptions)
  }
  componentWillReceiveProps() {
  	// 4. Updating state based on props
  	// 6. Side effects on props change
  	// 7. Fetching external data when props change
  }
  shouldComponentUpdate() {
  }
  componentWillUpdate(nextProps, nextState) {
  	// 5. Invoking external callbacks
  	// 8. Reading DOM properties before an update
  	
  }
  render() {
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
	8. Reading DOM properties before an update
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
	5. Invoking external callbacks
	6. Side effects on props change
  }
  
  componentWillUnmount() {
  }

複製代碼

最後

上面的內容基本就是結合個人一些經驗半翻譯半總結,有不許確的地方歡迎指正。

對更多細節感興趣的話能夠去看官方文檔,https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html

對async-rendering或者對dan小哥哥感興趣的話能夠去看看他在前端大會上的一段小演示:https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html

相關文章
相關標籤/搜索