国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Hooks 與 React 生命周期的關系

oliverhuang / 411人閱讀

摘要:更新階段卸載階段兄弟節點之間的通信主要是經過父組件和也是通過改變父組件傳遞下來的實現的,滿足的設計遵循單向數據流模型,因此任何兩個組件之間的通信,本質上都可以歸結為父子組件更新的情況。

你真的了解 React 生命周期嗎?

React 生命周期很多人都了解,但通常我們所了解的都是 單個組件 的生命周期,但針對 Hooks 組件、多個關聯組件(父子組件和兄弟組件) 的生命周期又是怎么樣的喃?你有思考和了解過嗎,接下來我們將完整的了解 React 生命周期。

關于 組件 ,我們這里指的是 React.Component 以及 React.PureComponent ,但是否包括 Hooks 組件喃?

一、Hooks 組件

函數組件 的本質是函數,沒有 state 的概念的,因此不存在生命周期一說,僅僅是一個 render 函數而已。

但是引入 Hooks 之后就變得不同了,它能讓組件在不使用 class 的情況下擁有 state,所以就有了生命周期的概念,所謂的生命周期其實就是 useStateuseEffect()useLayoutEffect()

即:Hooks 組件(使用了Hooks的函數組件)有生命周期,而函數組件(未使用Hooks的函數組件)是沒有生命周期的

下面,是具體的 class 與 Hooks 的生命周期對應關系

constructor:函數組件不需要構造函數,我們可以通過調用 useState 來初始化 state。如果計算的代價比較昂貴,也可以傳一個函數給 useState

const [num, UpdateNum] = useState(0)

getDerivedStateFromProps:一般情況下,我們不需要使用它,我們可以在渲染過程中更新 state,以達到實現 getDerivedStateFromProps 的目的。

function ScrollView({row}) {
  let [isScrollingDown, setIsScrollingDown] = useState(false);
  let [prevRow, setPrevRow] = useState(null);

  if (row !== prevRow) {
    // Row 自上次渲染以來發生過改變。更新 isScrollingDown。
    setIsScrollingDown(prevRow !== null && row > prevRow);
    setPrevRow(row);
  }

  return `Scrolling down: ${isScrollingDown}`;
}

React 會立即退出第一次渲染并用更新后的 state 重新運行組件以避免耗費太多性能。

shouldComponentUpdate:可以用?React.memo?包裹一個組件來對它的 props 進行淺比較

const Button = React.memo((props) => {
  // 具體的組件
});

注意:React.memo?等效于?PureComponent,它只淺比較 props。這里也可以使用 useMemo 優化每一個節點。

render:這是函數組件體本身。

componentDidMount, componentDidUpdate:?useLayoutEffect?與它們兩的調用階段是一樣的。但是,我們推薦你一開始先用?useEffect,只有當它出問題的時候再嘗試使用?useLayoutEffectuseEffect 可以表達所有這些的組合。

// componentDidMount
useEffect(()=>{
  // 需要在 componentDidMount 執行的內容
}, [])

useEffect(() => { 
  // 在 componentDidMount,以及 count 更改時 componentDidUpdate 執行的內容
  document.title = `You clicked ${count} times`; 
  return () => {
    // 需要在 count 更改時 componentDidUpdate(先于 document.title = ... 執行,遵守先清理后更新)
    // 以及 componentWillUnmount 執行的內容       
  } // 當函數中 Cleanup 函數會按照在代碼中定義的順序先后執行,與函數本身的特性無關
}, [count]); // 僅在 count 更改時更新

請記得 React 會等待瀏覽器完成畫面渲染之后才會延遲調用?useEffect,因此會使得額外操作很方便

componentWillUnmount:相當于 useEffect 里面返回的 cleanup 函數

// componentDidMount/componentWillUnmount
useEffect(()=>{
  // 需要在 componentDidMount 執行的內容
  return function cleanup() {
    // 需要在 componentWillUnmount 執行的內容      
  }
}, [])

componentDidCatch and getDerivedStateFromError:目前還沒有這些方法的 Hook 等價寫法,但很快會加上。

為方便記憶,大致匯總成表格如下。

class 組件 Hooks 組件
constructor useState
getDerivedStateFromProps useState 里面 update 函數
shouldComponentUpdate useMemo
render 函數本身
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount useEffect 里面返回的函數
componentDidCatch
getDerivedStateFromError
二、單個組件的生命周期 1. 生命周期
V16.3 之前

我們可以將生命周期分為三個階段:

掛載階段

組件更新階段

卸載階段

分開來講:

掛載階段

constructor:避免將 props 的值復制給 state

componentWillMount

render:react 最重要的步驟,創建虛擬 dom,進行 diff 算法,更新 dom 樹都在此進行

componentDidMount

組件更新階段

componentWillReceiveProps

shouldComponentUpdate

componentWillUpdate

render

componentDidUpdate

卸載階段

componentWillUnMount

這種生命周期會存在一個問題,那就是當更新復雜組件的最上層組件時,調用棧會很長,如果在進行復雜的操作時,就可能長時間阻塞主線程,帶來不好的用戶體驗,Fiber 就是為了解決該問題而生。

V16.3 之后

Fiber 本質上是一個虛擬的堆棧幀,新的調度器會按照優先級自由調度這些幀,從而將之前的同步渲染改成了異步渲染,在不影響體驗的情況下去分段計算更新。

對于異步渲染,分為兩階段:

reconciliation

componentWillMount

componentWillReceiveProps

shouldConmponentUpdate

componentWillUpdate

commit

componentDidMount

componentDidUpdate

其中,reconciliation 階段是可以被打斷的,所以 reconcilation 階段執行的函數就會出現多次調用的情況,顯然,這是不合理的。

所以 V16.3 引入了新的 API 來解決這個問題:

static getDerivedStateFromProps:?該函數在掛載階段和組件更新階段都會執行,即每次獲取新的propsstate 之后都會被執行在掛載階段用來代替componentWillMount;在組件更新階段配合 componentDidUpdate,可以覆蓋 componentWillReceiveProps 的所有用法。

同時它是一個靜態函數,所以函數體內不能訪問 this,會根據 nextPropsprevState 計算出預期的狀態改變,返回結果會被送給 setState返回 null 則說明不需要更新 state,并且這個返回是必須的

getSnapshotBeforeUpdate: 該函數會在?render?之后, DOM 更新前被調用,用于讀取最新的 DOM 數據。

返回一個值,作為 componentDidUpdate 的第三個參數;配合 componentDidUpdate, 可以覆蓋componentWillUpdate 的所有用法。

注意:V16.3 中只用在組件掛載或組件 props 更新過程才會調用,即如果是因為自身 setState 引發或者forceUpdate 引發,而不是由父組件引發的話,那么static getDerivedStateFromProps也不會被調用,在 V16.4 中更正為都調用。

即更新后的生命周期為:

掛載階段

constructor

static getDerivedStateFromProps

render

componentDidMount

更新階段

static getDerivedStateFromProps

shouldComponentUpdate

render

getSnapshotBeforeUpdate

componentDidUpdate

卸載階段

componentWillUnmount

2. 生命周期,誤區

誤解一:getDerivedStateFromProps?和?componentWillReceiveProps?只會在 props 改變 時才會調用

實際上,只要父級重新渲染,getDerivedStateFromProps?和?componentWillReceiveProps?都會重新調用,不管 props 有沒有變化。所以,在這兩個方法內直接將 props 賦值到 state 是不安全的。

// 子組件
class PhoneInput extends Component {
  state = { phone: this.props.phone };

  handleChange = e => {
    this.setState({ phone: e.target.value });
  };

  render() {
    const { phone } = this.state;
    return ;
  }

  componentWillReceiveProps(nextProps) {
    // 不要這樣做。
    // 這會覆蓋掉之前所有的組件內 state 更新!
    this.setState({ phone: nextProps.phone });
  }
}

// 父組件
class App extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    // 使用了 setInterval,
    // 每秒鐘都會更新一下 state.count
    // 這將導致 App 每秒鐘重新渲染一次
    this.interval = setInterval(
      () =>
        this.setState(prevState => ({
          count: prevState.count + 1
        })),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return (
      <>
        

Start editing to see some magic happen :)

This component will re-render every second. Each time it renders, the text you type will be reset. This illustrates a derived state anti-pattern.

); } }

實例可點擊這里查看

當然,我們可以在 父組件App 中 shouldComponentUpdate 比較?props 的 email 是不是修改再決定要不要重新渲染,但是如果子組件接受多個 props(較為復雜),就很難處理,而且 shouldComponentUpdate 主要是用來性能提升的,不推薦開發者操作 shouldComponetUpdate(可以使用 React.PureComponet)。

我們也可以使用 在 props 變化后修改 state

class PhoneInput extends Component {
  state = {
    phone: this.props.phone
  };

  componentWillReceiveProps(nextProps) {
    // 只要 props.phone 改變,就改變 state
    if (nextProps.phone !== this.props.phone) {
      this.setState({
        phone: nextProps.phone
      });
    }
  }
  
  // ...
}

但這種也會導致一個問題,當 props 較為復雜時,props 與 state 的關系不好控制,可能導致問題

解決方案一:完全可控的組件

function PhoneInput(props) {
  return ;
}

完全由 props 控制,不派生 state

解決方案二:有 key 的非可控組件

class PhoneInput extends Component {
  state = { phone: this.props.defaultPhone };

  handleChange = event => {
    this.setState({ phone: event.target.value });
  };

  render() {
    return ;
  }
}

當?key?變化時, React 會創建一個新的而不是更新一個既有的組件

誤解二:將 props 的值直接復制給 state

應避免將 props 的值復制給 state

constructor(props) {
 super(props);
 // 千萬不要這樣做
 // 直接用 props,保證單一數據源
 this.state = { phone: props.phone };
}
三、多個組件的執行順序 1. 父子組件

掛載階段

兩個 階段:

階段,由父組件開始執行到自身的 render,解析其下有哪些子組件需要渲染,并對其中 同步的子組件 進行創建,按 遞歸順序 挨個執行各個子組件至 render,生成到父子組件對應的 Virtual DOM 樹,并 commit 到 DOM。

階段,此時 DOM 節點已經生成完畢,組件掛載完成,開始后續流程。先依次觸發同步子組件各自的 componentDidMount,最后觸發父組件的。

注意:如果父組件中包含異步子組件,則會在父組件掛載完成后被創建。

所以執行順序是:

父組件 getDerivedStateFromProps —> 同步子組件 getDerivedStateFromProps —> 同步子組件 componentDidMount —> 父組件 componentDidMount —> 異步子組件 getDerivedStateFromProps —> 異步子組件 componentDidMount

更新階段

React 的設計遵循單向數據流模型 ,也就是說,數據均是由父組件流向子組件。

階段,由父組件開始,執行

static getDerivedStateFromProps

shouldComponentUpdate

更新到自身的 `render`,解析其下有哪些子組件需要渲染,并對 **子組件** 進行創建,按 **遞歸順序** 挨個執行各個子組件至 `render`,生成到父子組件對應的 Virtual DOM 樹,并與已有的 Virtual DOM 樹 比較,計算出 **Virtual DOM 真正變化的部分** ,并只針對該部分進行的原生DOM操作。

階段,此時 DOM 節點已經生成完畢,組件掛載完成,開始后續流程。先依次觸發同步子組件以下函數,最后觸發父組件的。

getSnapshotBeforeUpdate()

componentDidUpdate()

React 會按照上面的順序依次執行這些函數,每個函數都是各個子組件的先執行,然后才是父組件的執行。

所以執行順序是:

父組件 getDerivedStateFromProps —> 父組件 shouldComponentUpdate —> 子組件 getDerivedStateFromProps —> 子組件 shouldComponentUpdate —> 子組件 getSnapshotBeforeUpdate —>  父組件 getSnapshotBeforeUpdate —> 子組件 componentDidUpdate —> 父組件 componentDidUpdate

卸載階段

componentWillUnmount(),順序為 父組件的先執行,子組件按照在 JSX 中定義的順序依次執行各自的方法

注意 :如果卸載舊組件的同時伴隨有新組件的創建,新組件會先被創建并執行完 render,然后卸載不需要的舊組件,最后新組件執行掛載完成的回調。

2. 兄弟組件

掛載階段

若是同步路由,它們的創建順序和其在共同父組件中定義的先后順序是 一致 的。

若是異步路由,它們的創建順序和 js 加載完成的順序一致。

更新階段、卸載階段

兄弟節點之間的通信主要是經過父組件(Redux 和 Context 也是通過改變父組件傳遞下來的?props?實現的),滿足React 的設計遵循單向數據流模型因此任何兩個組件之間的通信,本質上都可以歸結為父子組件更新的情況

所以,兄弟組件更新、卸載階段,請參考 父子組件

走在最后:走心推薦一個在線編輯工具:StackBlitz,可以在線編輯 Angular、React、TypeScript、RxJS、Ionic、Svelte項目

預告:后續將加入高階組件的生命周期,敬請期待小瓶子的下次更新。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106133.html

相關文章

  • React Hooks 解析(上):基礎

    摘要:第一次了解這項特性的時候,真的有一種豁然開朗,發現新大陸的感覺。為了解決這一痛點,才會有剪頭函數的綁定特性。它同時具備和三個生命周期函數的執行時機。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 React Hooks 是從 v16.8 引入的又一開創性的新特性。第一次了解這項特性...

    yy736044583 評論0 收藏0
  • React-hooks 簡介

    摘要:比如在條件判斷中使用,在循環,嵌套函數中使用,都會造成執行順序不一致的問題。而比如定時器,事件監聽。第一個參數的返回值,會在組件卸載時執行,相當于,可以清理定時器,移除事件監聽,取消一些訂閱。 什么是 Hooks? 不通過編寫類組件的情況下,可以在組件內部使用狀態(state) 和其他 React 特性(生命周期,context)的技術 Hooks 為什么會出現 在之前的 React ...

    incredible 評論0 收藏0
  • 從Mixin到hooks,談談對React16.7.0-alpha中即將引入hooks理解

    摘要:已經被廢除,具體缺陷可以參考二為了解決的缺陷,第二種解決方案是高階組件簡稱。我們定義了父組件,存在自身的,并且將自身的通過的方式傳遞給了子組件。返回一個標識該的變量,以及更新該的方法。 ??為了實現分離業務邏輯代碼,實現組件內部相關業務邏輯的復用,在React的迭代中針對類組件中的代碼復用依次發布了Mixin、HOC、Render props等幾個方案。此外,針對函數組件,在Reac...

    ZweiZhao 評論0 收藏0
  • 從Mixin到hooks,談談對React16.7.0-alpha中即將引入hooks理解

    摘要:已經被廢除,具體缺陷可以參考二為了解決的缺陷,第二種解決方案是高階組件簡稱。我們定義了父組件,存在自身的,并且將自身的通過的方式傳遞給了子組件。返回一個標識該的變量,以及更新該的方法。 ??為了實現分離業務邏輯代碼,實現組件內部相關業務邏輯的復用,在React的迭代中針對類組件中的代碼復用依次發布了Mixin、HOC、Render props等幾個方案。此外,針對函數組件,在Reac...

    funnyZhang 評論0 收藏0
  • 從Mixin到hooks,談談對React16.7.0-alpha中即將引入hooks理解

    摘要:已經被廢除,具體缺陷可以參考二為了解決的缺陷,第二種解決方案是高階組件簡稱。我們定義了父組件,存在自身的,并且將自身的通過的方式傳遞給了子組件。返回一個標識該的變量,以及更新該的方法。 ??為了實現分離業務邏輯代碼,實現組件內部相關業務邏輯的復用,在React的迭代中針對類組件中的代碼復用依次發布了Mixin、HOC、Render props等幾個方案。此外,針對函數組件,在Reac...

    wizChen 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<