本文是本人在學習ReactHooks記錄的學習筆記,內容不只限於文檔中的內容,涉及了Hooks源碼相關。若是有錯誤,還請及時指正。javascript
useState能夠在函數組件中,添加state Hook。java
調用useState會返回一個state變量,以及更新state變量的方法。useState的參數是state變量的初始值,初始值僅在初次渲染時有效。react
更新state變量的方法,並不會像this.setState同樣,合併state。而是替換state變量。ajax
下面是一個簡單的例子, 會在頁面上渲染count的值,點擊setCount的按鈕會更新count的值。spring
若是新的state須要以前的state計算獲得。能夠向useState返回的更新函數中,傳遞一個函數。函數的參數是前一個state。api
useState的初始值是惰性的,只會在初次渲染組件的時候起做用。若是state的初始值需用經過複雜計算獲得,useState的初始值也能夠是一個函數,函數的返回值將是useState的初始值。數組
調用state的更新函數,傳入和當前同樣的state時。React會跳過子組件的渲染,以及effect的執行。瀏覽器
除了官方文檔外,推薦閱讀一下Um guia completo para useEffect性能優化
useEffect可讓咱們在函數組件中執行反作用操做。事件綁定,數據請求,動態修改DOM。
useEffect將會在每一次React渲染以後執行。不管是初次掛載時,仍是更新。(固然這種行爲咱們能夠控制)
在傳統的class組件中,一般在componentDidMount中添加對事件的監聽。在componentWillUnmount中會清除對事件的監聽。 咱們須要在不一樣的生命週期函數中,拆分咱們的邏輯。
而effect能夠返回一個函數,當react進行清除時, 會執行這個返回的函數。每當執行本次的effect時,都會對上一個effect進行清除。組件卸載時也會執行進行清除。
也就是說,下面的代碼中。每一次更新,都會對上一次的effect進行卸載,並執行本次的effect。
每次執行effect,清除上一次effect可能會形成沒必要要的性能浪費。咱們能夠經過effect的第二個參數,控制effect的執行。 第二個參數是useEffect的依賴,只有當依賴發生變化時,useEffect纔會更新。
當咱們傳遞傳遞一個空數組做爲依賴時,會告訴React,effect不依賴任何state或者props。咱們可使用此行爲模擬componentDidMount或者componentWillUnmount。
⚠️ 請記住使用空數組的effect和componentDidMount是有差別的
相似的問題,爲何有時候在effect裏拿到的是舊的state或prop呢?
useEffect會捕獲props, state,可是始終是初始的值。 如上圖所示,當咱們點擊button屢次,effect在3秒以後的回調,打印的依然是state的初始值。
這是由於javascript閉包的機制,函數組件被調用後,函數內部的state因爲被內部定時器的回調所依賴,因此沒有被垃圾回收機制所清除,而是保存在內存裏了。因此等定時器的回掉執行時,打印的是以前閉包中存儲的變量。
閱讀這篇文章是一個不錯的選擇How to fetch data with React Hooks?
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.. **
解決方案以下👇
咱們藉助preact的源碼進行分析,在preact中Hook的存儲在組件的私有屬性__hooks._list的數組中。讀取和存儲都依賴currentIndex的指針,若是hook的執行順序改變,currentIndex也會被改變,獲取的hook多是完成錯誤的。
下面是一個自定義Hook的🌰
咱們經過ajax請求表哥數據時,不少邏輯都是通用的。好比loading的狀態的處理,錯誤信息的處理,翻頁的處理。咱們能夠把這些邏輯抽象成一個公共的Hook。不一樣的api,做爲自定義Hook的參數。
下面是一個數據請求自定義Hook的例子:
如何使用?從自定義Hook向外暴露一些state,和setState。當page發生改變時,會從新請求數據。
接收一個context對象,並返回當前的context的值。useContext能夠訂閱context的變化。可是仍然須要上層組件使用<MyContext.Provider>來爲下層組件提供context。
useReducer接收三個參數,reducer函數,initialArg初始值,init惰性初始值函數。reducer函數和Redux的reducer相似,接收state,以及action。返回更新後的state。若是傳入三個參數,init(initialArg)將做爲初始值。
useReducer在複雜場景下比useState更適用。
useReducer的第二個參數能夠指定初始的state
若是指定useReducer的第三個參數,useReducer的初始值會被設置爲init(initialArg)
若是ReducerHook的返回的state與當前state相同,React將跳過子組件的渲染及effect的執行。
在自定義Hook的例子中,使用了多個useState進行狀態管理,當出現大量狀態時,useState會使得邏輯變得很複雜。咱們如今可使用useReducer管理多個state,也可向子組件傳遞dispatch,而不是回調函數。
下面是將自定義Hook中,請求數據自定義Hook的例子,改形成useReducer的方法。
useCallback接收回調函數和依賴數組做爲參數。useCallback會返回memoized函數。當依賴項改變的時候,會返回的新的memoized函數。
useMemo和useCallback相似。useMemo會返回memoized值。當依賴項改變時,會從新計算memoized值。
useRef除了獲取dom節點的功能外,useRef的current屬性,能夠方便保存任何可變值。useRef每一次渲染時,都會返回同一個ref對象。
設想下面👇這種狀況, 組件從新渲染時,因爲timer發生了變化,咱們會永遠沒法清除定時器。
若是想要在更新後,依然能夠清除定時器,能夠將timer,保存到useRef的current屬性上
useLayoutEffect和useEffect相似,可是不一樣的是: