「學習筆記」ReactHooks入門

📖前言

本文是本人在學習ReactHooks記錄的學習筆記,內容不只限於文檔中的內容,涉及了Hooks源碼相關。若是有錯誤,還請及時指正。javascript

❓什麼是Hook?爲何須要Hook?

推薦看一下Dan Abramov的博客html

image

  1. 邏輯複用,若是使用高階組件等特性,較爲複雜。
  2. 傳統的函數組件沒法存儲state狀態。
  3. Hooks容許在函數組件中,調用React的功能。
  4. 使用自定義Hook能夠簡化邏輯複用。
  5. Hooks是React的將來
  6. Hooks不是什麼魔法。Hooks的設計也與React無關。

⛰️useState

useState能夠在函數組件中,添加state Hook。java

調用useState會返回一個state變量,以及更新state變量的方法。useState的參數是state變量的初始值,初始值僅在初次渲染時有效react

更新state變量的方法,並不會像this.setState同樣,合併state。而是替換state變量。ajax

下面是一個簡單的例子, 會在頁面上渲染count的值,點擊setCount的按鈕會更新count的值。spring

image

函數式更新

若是新的state須要以前的state計算獲得。能夠向useState返回的更新函數中,傳遞一個函數。函數的參數是前一個state。api

image

惰性的初始值

useState的初始值是惰性的,只會在初次渲染組件的時候起做用。若是state的初始值需用經過複雜計算獲得,useState的初始值也能夠是一個函數,函數的返回值將是useState的初始值。數組

image

跳過state更新

調用state的更新函數,傳入和當前同樣的state時。React會跳過子組件的渲染,以及effect的執行。瀏覽器

image

小節

  1. useState的更新是替換,而不是合併。
  2. useState能夠接收函數參數,並將函數的返回值做爲初始值
  3. useState的更新函數能夠接收函數做爲參數,函數的參數是前一狀態的state值。
  4. 使用當前的值,對state進行更新不會觸發渲染。
  5. 可使用多個useState,聲明多個state變量。

🏁useEffect

除了官方文檔外,推薦閱讀一下Um guia completo para useEffect性能優化

useEffect可讓咱們在函數組件中執行反作用操做。事件綁定,數據請求,動態修改DOM。

useEffect將會在每一次React渲染以後執行。不管是初次掛載時,仍是更新。(固然這種行爲咱們能夠控制)

image

須要清除的effect

在傳統的class組件中,一般在componentDidMount中添加對事件的監聽。在componentWillUnmount中會清除對事件的監聽。 咱們須要在不一樣的生命週期函數中,拆分咱們的邏輯。

而effect能夠返回一個函數,當react進行清除時, 會執行這個返回的函數。每當執行本次的effect時,都會對上一個effect進行清除。組件卸載時也會執行進行清除。

也就是說,下面的代碼中。每一次更新,都會對上一次的effect進行卸載,並執行本次的effect。

image

effect性能優化

每次執行effect,清除上一次effect可能會形成沒必要要的性能浪費。咱們能夠經過effect的第二個參數,控制effect的執行。 第二個參數是useEffect的依賴,只有當依賴發生變化時,useEffect纔會更新。

image

當咱們傳遞傳遞一個空數組做爲依賴時,會告訴React,effect不依賴任何state或者props。咱們可使用此行爲模擬componentDidMount或者componentWillUnmount。

⚠️ 請記住使用空數組的effect和componentDidMount是有差別的

image

🤔️useEffect(fn, [])和componentDidMount的差別有什麼差別?

相似的問題,爲何有時候在effect裏拿到的是舊的state或prop呢?

image

useEffect會捕獲props, state,可是始終是初始的值。 如上圖所示,當咱們點擊button屢次,effect在3秒以後的回調,打印的依然是state的初始值。

爲何會這樣?

這是由於javascript閉包的機制,函數組件被調用後,函數內部的state因爲被內部定時器的回調所依賴,因此沒有被垃圾回收機制所清除,而是保存在內存裏了。因此等定時器的回掉執行時,打印的是以前閉包中存儲的變量。

如何避免拿到舊的prop或者state?

  1. 使用useRef
  2. 查看是否遺漏了依賴, 致使effect沒有更新

🤔️如何正確的useEffect中請求數據?

閱讀這篇文章是一個不錯的選擇How to fetch data with React Hooks?

如何請求數據?

image

如何在useEffect中使用async/await?

async函數默認會返回一個Promise對象,而useEffect中,只容許什麼都不返回或者返回一個清除函數。控制檯中,會觸發以下警告 **Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.. **

解決方案以下👇

image

小節

  1. 可使用多個useEffect分離邏輯。在class組件中,咱們一般會在componentDidMount中,混雜添加事件綁定,請求數據等多種無關的邏輯。
  2. 若是隻須要useEffect在加載時執行一次,或者卸載組件時執行一次,能夠向useEffect傳遞一個空數組。
  3. useEffect不支持直接使用async函數
  4. 若是在useEffect中造成了閉包,將會拿到舊的props,或者state。(這個與Hook無關,與函數組件自己的特性相關)
  5. useEffect返回的函數,清除函數。會在上一個effect被清除時調用。

📰Hook規則

  1. 只在最頂層使用Hook,不在判斷,循環語句中使用Hook
  2. 只在React函數中使用Hook

🤔️爲何Hook高度依賴執行順序?

咱們藉助preact的源碼進行分析,在preact中Hook的存儲在組件的私有屬性__hooks._list的數組中。讀取和存儲都依賴currentIndex的指針,若是hook的執行順序改變,currentIndex也會被改變,獲取的hook多是完成錯誤的。

image

⚙️自定義Hook

  1. 自定義Hook必須以use開頭
  2. 自定義Hook只是邏輯複用,其中的state是不會共享的。
  3. 自定義Hook內部能夠調用其餘Hook。
  4. 避免過早的拆分抽象邏輯

下面是一個自定義Hook的🌰

💡自定義數據獲取Hook

咱們經過ajax請求表哥數據時,不少邏輯都是通用的。好比loading的狀態的處理,錯誤信息的處理,翻頁的處理。咱們能夠把這些邏輯抽象成一個公共的Hook。不一樣的api,做爲自定義Hook的參數。

下面是一個數據請求自定義Hook的例子:

image

如何使用?從自定義Hook向外暴露一些state,和setState。當page發生改變時,會從新請求數據。

image

🚀其餘API

useContext

接收一個context對象,並返回當前的context的值。useContext能夠訂閱context的變化。可是仍然須要上層組件使用<MyContext.Provider>來爲下層組件提供context。

image

useReducer

useReducer接收三個參數,reducer函數,initialArg初始值,init惰性初始值函數。reducer函數和Redux的reducer相似,接收state,以及action。返回更新後的state。若是傳入三個參數,init(initialArg)將做爲初始值。

useReducer在複雜場景下比useState更適用。

指定初始state

useReducer的第二個參數能夠指定初始的state

惰性初始化

若是指定useReducer的第三個參數,useReducer的初始值會被設置爲init(initialArg)

image

跳過dispatch

若是ReducerHook的返回的state與當前state相同,React將跳過子組件的渲染及effect的執行。

🌰例子

在自定義Hook的例子中,使用了多個useState進行狀態管理,當出現大量狀態時,useState會使得邏輯變得很複雜。咱們如今可使用useReducer管理多個state,也可向子組件傳遞dispatch,而不是回調函數。

下面是將自定義Hook中,請求數據自定義Hook的例子,改形成useReducer的方法。

image

useCallback

useCallback接收回調函數和依賴數組做爲參數。useCallback會返回memoized函數。當依賴項改變的時候,會返回的新的memoized函數。

image

useMemo

useMemo和useCallback相似。useMemo會返回memoized值。當依賴項改變時,會從新計算memoized值。

image

🌟useRef

useRef除了獲取dom節點的功能外,useRef的current屬性,能夠方便保存任何可變值。useRef每一次渲染時,都會返回同一個ref對象。

設想下面👇這種狀況, 組件從新渲染時,因爲timer發生了變化,咱們會永遠沒法清除定時器。

image

若是想要在更新後,依然能夠清除定時器,能夠將timer,保存到useRef的current屬性上

image

useLayoutEffect

image
(紅圈中是同步的操做)

useLayoutEffect和useEffect相似,可是不一樣的是:

  • useEffect,使用useEffect不會阻塞瀏覽器的重繪
  • useLayoutEffect, 使用useLayoutEffect,會阻塞瀏覽器的重繪。若是你須要手動的修改Dom,推薦使用useLayoutEffect。由於若是在useEffect中更新Dom,useEffect不會阻塞重繪,用戶可能會看到由於更新致使的閃爍,

image

🚗參考

相關文章
相關標籤/搜索