人在身處逆境時,適應環境的能力實在驚人。人可以忍受不幸,也可以戰勝不幸,因為人有著驚人的潛力,只要立志發揮它,就一定能渡過難關。
Hooks 是 React 16.8 的新增特性。它可以讓你在不編寫 class 元件的情況下使用 state 以及其他的 React 特性。
React Hooks 表現形式是以 use
開頭的函式被稱為 Hook。useState
是 React 提供的一個內建 Hook。你可以在 React API 參考 中找到其他內建的 Hook。你也可以透過組合現有的 Hook 來編寫屬於你自己的 Hook。
跟普通函式相比,Hook 比普通函式更為嚴格。你只能在你的元件(或其他 Hook)的頂層呼叫 Hook。如果你想在一個條件或迴圈中使用 useState
,請提取一個新的元件並在元件內部使用它。
為什麼要使用 Hooks?
- 解決高階類元件複用,導致程式碼層級複雜問題。
- React 元件生命週期的複雜,用於代替生命週期函式。
- 如果一個類一開始設計成了 function 元件,無狀態元件,因為需要狀態,又改成了 class 成本高。
1. State Hook
State 讓元件“記住”使用者輸入之類的資訊。例如,表單元件可以使用 state 來儲存輸入值,而圖片庫元件可以使用 state 來儲存所選的影像索引。要給元件新增狀態,可以使用以下介紹的鉤子之一。
1.1 useState
useState 是一個 React Hook。它允許你向元件新增一個狀態變數。模擬類元件的狀態管理。
// usss
const [state, setstate] = usestate(initialState)
1.2 useReducer
useReducer 是一個 React Hook,它允許你向元件裡面新增一個 reducer,來維護一個狀態 state。useReducer 不支援非同步處理,非同步處理需要藉助於 Hooks useEffect。
在單個元件中實現狀態管理,理解 useReducer 的使用。
const [state, dispatch] = useReducer(reducer, initialState)
其次配合 useContext 跨級通訊。將資料邏輯(即狀態管理)從元件中(檢視邏輯)分離出來透過外部管理,降低元件耦合度。對於單個元件來說意義不大,但是對於多個元件資料需要共享狀態的時候很方便。
2. Context Hook
2.1 useContext
useContext 是一個 React Hook,可以讓你讀取和訂閱元件中的 context。減少元件層級,便於元件跨級通訊處理。
useContext 配合 useState、useContext 配合 useReducer。
3. Ref Hooks
Refs 允許元件儲存一些不用於呈現(渲染)的資訊,比如 DOM 節點或超時 ID。與 state 不同,更新 ref 並不會重新呈現元件。React 提供了 useRef hook 來描述。
3.1 useRef
儲存引用值:
const ref = useRef(initialValue)
4. Effect Hooks
Effect 允許元件 連線到外部系統並與之同步。這包括處理網路、瀏覽器、DOM、動畫、使用不同 UI 庫編寫的 widgets 以及其他非 React 程式碼。
Effect Hooks 副作用鉤子函式很好模擬了類元件的生命週期函式。但是我們知道 Function Component 不存在生命週期,所以不要把 Class Component 的生命週期概念搬過夾試圖對號入座。
4.1 useEffect
useEffect
是一個 React Hook,它允許你 連線到外部系統並與之同步。
useEffect(setup, dependencies?)
// 處理副作用
useEffect(() => {
// 副作用函式處理程式碼塊 { 比如資料請求,定時器 }
console.log("effect")
return () => {
console.log("cleanup")
// cleanup
// 執行時機:無依賴情況下:元件銷燬執行 有依賴情況下:依賴更新和元件銷燬的時候
}
}, [ name /* 依賴的狀態,如果為空表示不依賴任何狀態 */])
依賴:不要對 Dependencies 撒謊,如果你明明在 effect 中使用了某個變數,卻沒有申明在依賴中,你等於向 React 撒了謊,後果就是,當依賴的變數改變時,useEffect 也不會再次執行,eslint 會報警告。
4.2 useLayoutEffect
useLayoutEffect
是 useEffect
的一個版本,在瀏覽器重新繪製螢幕之前觸發。useLayoutEffect
內部的程式碼和所有計劃的狀態更新阻塞了瀏覽器重新繪製螢幕。如果過度使用,這會使你的應用程式變慢。如果可能的話,儘量選擇 useEffect
。
然後如果 Effect 一定要阻止瀏覽器繪製螢幕,使用 useLayoutEffect
替換 useEffect
。請注意,絕大多數的 Effect 都不需要這樣。只有當在瀏覽器繪製之前執行 Effect 非常重要的時候才需要如此:例如,在使用者看到 tooltip 之前測量並定位它。
4.3 useInsertionEffect
useInsertionEffect
是為 CSS-in-JS 庫的作者特意打造的。除非你正在使用 CSS-in-JS 庫並且需要注入樣式,否則你應該使用 useEffect
或者 useLayoutEffect
。
4.4 Effect Hooks 之間區別
簡單來說就是呼叫時機不同,useLayoutEFfect 和原來 componentDidMount & componentDidUpdate 一致,在 React 完成 Dom 更新後馬上同步呼叫的程式碼,即在瀏覽器重新繪製螢幕之前觸發 useLayouteEffect。你可以在這裡測量佈局。會阻塞頁面渲染(渲染樹)。
useEffect 是會在整個頁面渲染完才會呼叫的程式碼。
useInsertionEffect 會在 React 修改 DOM 之前觸發。可以在這裡插入動態 CSS。
官方建議優先使用 useEffect
However, we recommend starting with useEffect first and only trying useLayoutEffect if that causes a problem.
在實際使用時如果想避免頁面抖動(即在 useEffect 裡修改 Dom 很有可能出現)的話,可以把需要操作 Dom 的程式碼放在 useLayoutEffect 裡。在這裡做點 Dom 操作,這些 Dom 修改會和 react 做出的更改一起被一次性渲染到螢幕上,只有一次迴流、重繪的代價。
5. 效能 Hook
5.1 useMemo
useMemo
是一個 React Hook,它在每次重新渲染的時候能夠快取、使用計算的結果。
const cachedValue = useMemo(calculateValue, dependencies)
useMemo(() => first, [second])
記憶純函式計算結果、跳過元件的重新渲染,記憶一個函式。
對於使用 useMemo 記憶一個函式,這看起來很笨拙!記憶函式很常見,React 有一個專門用於此的內建 Hook。將你的函式包裝到 useCallback
而不是 useMemo
中,以避免編寫額外的巢狀函式。 即 useCallback
的唯一好處是它可以讓你避免在內部編寫額外的巢狀函式。它沒有做任何其他事情。
5.2 useCallBack
useCallback
是一個允許你在多次渲染中快取函式的 React Hook。記憶函式,防止因為元件重新渲染,導致元件內部定義的方法被重新建立,起到快取作用。只有第二個引數依賴項變化了才重新宣告一次。如果依賴傳入空陣列,那麼函式第一次建立後就被快取,這時如果函式定義中的使用了某個狀態值改變了,以後呼叫函式拿到的還是老的值。如果不傳第二個引數,每次都會重新宣告一次。
const cachedFn = useCallback(fn, dependencies)
和 useMemo 唯一的區別是:useCallback 不會執行第一個引數函式,而是將它返回給你,而 useMemo 會執行第一個引數函式並且將函式執行結果返回給你。所以在前面的例子中,可以返回 handleClick 來達到儲存函式的目的。
所以 useCallback 常用記憶事件函式,生成記憶後的事件函式並傳遞給子元件使用。而 useMemo 更適合經過函式計算得到一個確定的值,比如記憶元件、結果。
5.3 useTransition
useTransition
允許將狀態轉換標記為非阻塞,並允許其他更新中斷它此更新。
5.4 useDeferredValued
useDeferredValue
是一個 React Hook,可以讓你延遲更新 UI 的某些部分。
6. 資源 Hook
use
是一個 React Hook,它可以讓你讀取類似於 Promise 或 context 的資源的值。
7. 其他 Hook
這些 Hook 主要對庫作者有用,而不常用於應用程式程式碼。
useDebugValue
允許在 React 開發者工具中為自定義 Hook 新增一個標籤。
useId
允許元件繫結一個唯一 ID,其通常與可訪問性 API 一起使用。
useSyncExternalStore
允許一個元件訂閱一個外部 store。
8. 自定義 Hooks
當我們想在兩個函式之間共享邏輯時,我們會把它提取到第三個函式中。即抽離複用 js 邏輯,讓結構更加清晰,符合函數語言程式設計思想。
必須以 'use' 開頭嗎?必須如此。這個約定非常重要。不遵循的話,由於無法判斷某個函式是否包含對其內部 Hook 的呼叫,React 將無法自動檢查你的 Hook 是否違反了 Hook 的規則。官網主要研究以下幾個問題:
- 什麼是自定義鉤子,如何編寫自己的鉤子;
- 如何命名和結構化自定義的鉤子;
- 何時以及為什麼要提取自定義鉤子;
- 如何在元件之間重用邏輯;