react打怪筆記

介紹

本文爲學習react中的記錄。不定時更新

Tips:

  • 當組件的propsstate有變化,執行render函數。
  • 無論是使用函數或是類來聲明一個組件,它決不能修改它自己的props
  • React 可以將多個setState() 調用合併成一個調用來提高性能。
本文首發於我的個人網站: Timbok.top

無狀態函數式組件 (stateless functional component)

React v16.7.0-alpha(內測) 將引入 Hooks, 這將意味着函數式組件將擁有類似類組件的特性。

無生命週期方法

函數式組件,有時也被稱爲無狀態組件,沒有任何生命週期方法,意味着每次上層組件樹狀態發生變更時它們都會重新渲染,這就是因爲缺少 shouldComponentUpdate 方法導致的。這也同樣意味着您不能定義某些基於組件掛載和卸載的行爲。

沒有 this 和 ref

更有趣的是您在函數式組件中既不能使用 this關鍵字或訪問到 ref。對於習慣了嚴格意義上的類或面向對象風格的人來說,這很讓他們驚訝。這也是使用函數最大的爭論點。
另一個有趣的事實就是您仍然可以訪問到 context,如果您將 context 定義爲函數的一個 props。

避免常見陷阱

在編寫無狀態函數式組件時,您需要避免某些特定的模式。避免在函數式組件中定義函數,這是因爲每一次函數式組件被調用的時候,一個新的函數都會被創建。

const Functional = ({...}) => {
  const handleSomething = e => path(['event', 'target'], e)
  return (
    // ...
  )
}

這個問題很容易解決,您可以將這個函數作爲 props 傳遞進去,或者將它定義在組件外面。

const handleSomething = e => path(['event', 'target'], e)
const Functional = ({...}) => // ...

生命週期

概況

react生命週期圖解

生命週期演示

1.初始化

在組件初始化階段會執行

  1. constructor
  2. static getDerivedStateFromProps()
  3. componentWillMount() / UNSAFE_componentWillMount()
  4. render()
  5. componentDidMount()

2.更新階段

propsstate的改變可能會引起組件的更新,組件重新渲染的過程中會調用以下方法:

  1. componentWillReceiveProps() / UNSAFE_componentWillReceiveProps()
  2. static getDerivedStateFromProps()
  3. shouldComponentUpdate()
  4. componentWillUpdate() / UNSAFE_componentWillUpdate()
  5. render()
  6. getSnapshotBeforeUpdate()
  7. componentDidUpdate()

3.卸載階段

  1. componentWillUnmount()

4.錯誤處理

  1. componentDidCatch()

詳解

1.constructor(props)

react組件的構造函數在掛載之前被調用。在實現React.Component構造函數時,需要先在添加其他內容前,調用super(props),用來將父組件傳來的props綁定到這個類中,使用this.props將會得到。

官方建議不要在constructor引入任何具有副作用和訂閱功能的代碼,這些應當在componentDidMount()中寫入。

constructor中應當做些初始化的動作,如:初始化state,將事件處理函數綁定到類實例上,但也不要使用setState()。如果沒有必要初始化state或綁定方法,則不需要構造constructor,或者把這個組件換成純函數寫法。

當然也可以利用props初始化state,在之後修改state不會對props造成任何修改,但仍然建議大家提升狀態到父組件中,或使用redux統一進行狀態管理。

constructor(props) {
  super(props);
  this.state = {
    color: props.initialColor
  };
}

2.static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps在組件實例化後,和接受新的props後被調用。他返回一個對象來更新狀態,或者返回null表示新的props不需要任何state的更新。

如果是由於父組件的props更改,所帶來的重新渲染,也會觸發此方法。

調用steState()不會觸發getDerivedStateFromProps()

3. componentWillMount() / UNSAFE_componentWillMount()

componentWillMount()將在react未來版本中被棄用。UNSAFE_componentWillMount()在組件掛載前被調用,在這個方法中調用setState()不會起作用,是由於他在render()前被調用。

爲了避免副作用和其他的訂閱,官方都建議使用componentDidMount()代替。這個方法是用於在服務器渲染上的唯一方法。

4.render()

render()方法是必需的。當他被調用時,他將計算this.propsthis.state,並返回以下一種類型:

  1. React元素。通過jsx創建,既可以是dom元素,也可以是用戶自定義的組件。
  2. 字符串或數字。他們將會以文本節點形式渲染到dom中。
  3. Portals。react 16版本中提出的新的解決方案,可以使組件脫離父組件層級直接掛載在DOM樹的任何位置。
  4. null,什麼也不渲染
  5. 布爾值。也是什麼都不渲染,通常後跟組件進行判斷。

當返回null,false,ReactDOM.findDOMNode(this)將會返回null,什麼都不會渲染。

render()方法必須是一個純函數,他不應該改變state,也不能直接和瀏覽器進行交互,應該將事件放在其他生命週期函數中。
如果shouldComponentUpdate()返回falserender()不會被調用。

Fragments

你也可以在render()中使用數組,如:(不要忘記給每個數組元素添加key,防止出現警告)

render() {
  return [
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

換一種寫法,可以不寫key(v16++)

render() {
  return (
    <React.Fragment>
      <li>First item</li>
      <li>Second item</li>
      <li>Third item</li>
    </React.Fragment>
  );
}

官方示例:

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

5.componentWillReceiveProps()/UNSAFE_componentWillReceiveProps(nextProps)

官方建議使用getDerivedStateFromProps函數代替componentWillReceiveProps()。當組件掛載後,接收到新的props後會被調用。如果需要更新state來響應props的更改,則可以進行this.propsnextProps的比較,並在此方法中使用this.setState()

如果父組件會讓這個組件重新渲染,即使props沒有改變,也會調用這個方法。

react不會在組件初始化props時調用這個方法。調用this.setState也不會觸發。

6.shouldComponentUpdate(nextProps, nextState)

調用shouldComponentUpdate使react知道,組件的輸出是否受stateprops的影響。默認每個狀態的更改都會重新渲染,大多數情況下應該保持這個默認行爲。

在渲染新的propsstate前,shouldComponentUpdate會被調用。默認爲true。這個方法不會在初始化時被調用,也不會在forceUpdate()時被調用。返回false不會阻止子組件在state更改時重新渲染。

如果shouldComponentUpdate()返回falsecomponentwillupdate,rendercomponentDidUpdate不會被調用。

在未來版本,shouldComponentUpdate()將會作爲一個提示而不是嚴格的指令,返回false仍然可能導致組件的重新渲染。
官方並不建議在shouldComponentUpdate()中進行深度查詢或使用JSON.stringify(),他效率非常低,並且損傷性能。

7.UNSAFE_componentWillUpdate(nextProps, nextState)

在渲染新的stateprops時,UNSAFE_componentWillUpdate會被調用,將此作爲在更新發生之前進行準備的機會。這個方法不會在初始化時被調用。

不能在這裏使用this.setState(),也不能做會觸發視圖更新的操作。如果需要更新stateprops,調用getDerivedStateFromProps

8.getSnapshotBeforeUpdate()

在react render()後的輸出被渲染到DOM之前被調用。它使您的組件能夠在它們被潛在更改之前捕獲當前值(如滾動位置)。這個生命週期返回的任何值都將作爲參數傳遞給componentDidUpdate()。

9.componentDidUpdate(prevProps, prevState, snapshot)

在更新發生後立即調用componentDidUpdate()。此方法不用於初始渲染。當組件更新時,將此作爲一個機會來操作DOM。只要您將當前的props與以前的props進行比較(例如,如果props沒有改變,則可能不需要網絡請求),這也是做網絡請求的好地方。

如果組件實現getSnapshotBeforeUpdate()生命週期,則它返回的值將作爲第三個“快照”參數傳遞給componentDidUpdate()。否則,這個參數是undefined

10.componentWillUnmount()

在組件被卸載並銷燬之前立即被調用。在此方法中執行任何必要的清理,例如使定時器無效,取消網絡請求或清理在componentDidMount()中創建的任何監聽。

11.componentDidCatch(error, info)

錯誤邊界是React組件,可以在其子組件樹中的任何位置捕獲JavaScript錯誤,記錄這些錯誤並顯示回退UI,而不是崩潰的組件樹。錯誤邊界在渲染期間,生命週期方法以及整個樹下的構造函數中捕獲錯誤。

如果類組件定義了此生命週期方法,則它將成爲錯誤邊界。在它中調用setState()可以讓你在下面的樹中捕獲未處理的JavaScript錯誤,並顯示一個後備UI。只能使用錯誤邊界從意外異常中恢復;不要試圖將它們用於控制流程。詳細

錯誤邊界只會捕獲樹中下面組件中的錯誤。錯誤邊界本身不能捕獲錯誤。

爲什麼虛擬dom會提高性能?

虛擬dom相當於在js和真實dom中間加了一個緩存,利用dom diff算法避免了沒有必要的dom操作,從而提高性能。

具體實現步驟如下:

用 JavaScript 對象結構表示 DOM 樹的結構;然後用這個樹構建一個真正的 DOM 樹,插到文檔當中

當狀態變更的時候,重新構造一棵新的對象樹。然後用新的樹和舊的樹進行比較,記錄兩棵樹差異把2所記錄的差異應用到步驟1所構建的真正的DOM樹上,視圖就更新了。

參考鏈接

參考文檔

  1. React v16.3.0: New lifecycles and context API
  2. React.Component
  3. mytac
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章