React Hooks 原理與最佳實(shí)踐
1. 前言
React Hooks 是 React 16.8 引入的新特性,允許我們?cè)诓皇褂?Class 的前提下使用 state 和其他特性。React Hooks 要解決的問(wèn)題是狀態(tài)共享,是繼 render-props 和 higher-order components 之后的第三種狀態(tài)邏輯復(fù)用方案,不會(huì)產(chǎn)生 JSX 嵌套地獄問(wèn)題。
2. 狀態(tài)邏輯復(fù)用
一般來(lái)說(shuō),組件是 UI 和邏輯,但是邏輯這一層面卻很難復(fù)用。對(duì)用戶而言,組件就像一個(gè)黑盒,我們應(yīng)該拿來(lái)即用。但當(dāng)組件的樣式或者結(jié)構(gòu)不滿足需求的時(shí)候,我們只能去重新實(shí)現(xiàn)這個(gè)組件。

在我們開發(fā) React 應(yīng)用的時(shí)候,經(jīng)常會(huì)遇到類似下面這種場(chǎng)景,你可能會(huì)有兩個(gè)疑問(wèn):
Loading 是否可以復(fù)用? Loading 該怎么復(fù)用? 
這幾個(gè)例子都指向了同一個(gè)問(wèn)題,那就是如何實(shí)現(xiàn)組件的邏輯復(fù)用?
2.1 render props
將函數(shù)作為 props 傳給父組件,父組件中的狀態(tài)共享,通過(guò)參數(shù)傳給函數(shù),實(shí)現(xiàn)渲染,這就是 render props。使用 render prop 的庫(kù)有 React Router、Downshift 以及 Formik。以下面這個(gè) Toggle 組件為例子,我們一般可以這樣用:

可以看到,控制 Modal 組件是否展示的狀態(tài)被提取到了 Toggle 組件中,這個(gè) Toggle 組件還可以拿來(lái)多次復(fù)用到其他組件里面。那么這個(gè) Toggle 是怎么實(shí)現(xiàn)的呢?看到實(shí)現(xiàn)后你就會(huì)理解 render props 的原理

關(guān)于 render props 的更多內(nèi)容可以參考 React 中文網(wǎng)的相關(guān)章節(jié):Render Props
2.2 higher-order components
higher-order components 一般簡(jiǎn)稱 hoc,中文翻譯為高階組件。從名字上就可以看出來(lái),高階組件肯定和高階函數(shù)有什么千絲萬(wàn)縷的關(guān)系。高階組件的本質(zhì)是一個(gè)高階函數(shù),它接收一個(gè)組件,返回一個(gè)新的組件。在這個(gè)新的組件中的狀態(tài)共享,通過(guò) props 傳給原來(lái)的組件。以剛剛那個(gè) Toggle 組件為例子,高階組件同樣可以被多次復(fù)用,常常可以配合裝飾器一起使用。

高階組件的實(shí)現(xiàn)和 render props 也不太一樣,主要是一個(gè)高階函數(shù)。

2.3 render props 和高階組件的弊端
不管是 render props 還是高階組件,他們要做的都是實(shí)現(xiàn)狀態(tài)邏輯的復(fù)用,可這倆是完美的解決方案嗎?考慮一下,如果我們依賴了多個(gè)需要復(fù)用的狀態(tài)邏輯的時(shí)候,該怎么寫呢?以 render props 為例:

看看這個(gè)代碼,你有沒有一種似曾相識(shí)的感覺?這一天,我們終于想起被“回調(diào)地獄”支配的恐懼。不得不再次祭出這張圖了。

同樣地,高階組件也會(huì)有這個(gè)問(wèn)題,但由于裝飾器的簡(jiǎn)潔性,沒有 render props 看起來(lái)那么可怕。除此之外,他們倆還有另一個(gè)問(wèn)題,那就是組件嵌套過(guò)深之后,會(huì)給調(diào)試帶來(lái)很大的麻煩。這個(gè)是 render props 中組件嵌套在 React 開發(fā)者工具中的表現(xiàn)。

對(duì)于高階組件來(lái)說(shuō),如果你沒有對(duì)組件手動(dòng)設(shè)置 name/displayName,就會(huì)遇到更嚴(yán)重的問(wèn)題,那就是一個(gè)個(gè)匿名組件嵌套。畢竟上面 render props 的嵌套至少能知道組件名。

社區(qū)里面也已經(jīng)有很多解決 render props 嵌套的方案,其中 Epitath 提供了一種以 generator 的方法來(lái)解決嵌套問(wèn)題,利用 generator 實(shí)現(xiàn)了偽同步代碼。

更多細(xì)節(jié)可以參考黃子毅的這篇文章:精讀《Epitath 源碼 - renderProps 新用法》
2.4 React Hooks
React Hooks 則可以完美解決上面的嵌套問(wèn)題,它擁有下面這幾個(gè)特性。
多個(gè)狀態(tài)不會(huì)產(chǎn)生嵌套,寫法還是平鋪的
允許函數(shù)組件使用 state 和部分生命周期
更容易將組件的 UI 與狀態(tài)分離

上面是一個(gè)結(jié)合了 useState 和 useEffect 兩個(gè) hook 方法的例子,主要是在 resize 事件觸發(fā)時(shí)獲取到當(dāng)前的 window.innerWidth。這個(gè) useWindowWidth 方法可以拿來(lái)在多個(gè)地方使用。常用的 Hook 方法如下:

3. useState & useRef
useState 是 React Hooks 中很基本的一個(gè) API,它的用法主要有這幾種:
useState 接收一個(gè)初始值,返回一個(gè)數(shù)組,數(shù)組里面分別是當(dāng)前值和修改這個(gè)值的方法(類似 state 和 setState)。 useState 接收一個(gè)函數(shù),返回一個(gè)數(shù)組。 setCount 可以接收新值,也可以接收一個(gè)返回新值的函數(shù)。
const [ count1, setCount1 ] = useState(0);const [ count2, setCount2 ] = useState(() => 0);setCount1(1); // 修改 state
3.1 和 class state 的區(qū)別
雖然函數(shù)組件也有了 state,但是 function state 和 class state 還是有一些差異:
function state 的粒度更細(xì),class state 過(guò)于無(wú)腦。 function state 保存的是快照,class state 保存的是最新值。 引用類型的情況下,class state 不需要傳入新的引用,而 function state 必須保證是個(gè)新的引用。
3.2 快照(閉包) vs 最新值(引用)
在開始前,先拋出這么一個(gè)問(wèn)題。在 1s 內(nèi)頻繁點(diǎn)擊10次按鈕,下面代碼的執(zhí)行表現(xiàn)是什么?

如果是這段代碼呢?它又會(huì)是什么表現(xiàn)?

如果你能成功答對(duì),那么恭喜你,你已經(jīng)掌握了 useState 的用法。在第一個(gè)例子中,連續(xù)點(diǎn)擊十次,頁(yè)面上的數(shù)字會(huì)從0增長(zhǎng)到10。而第二個(gè)例子中,連續(xù)點(diǎn)擊十次,頁(yè)面上的數(shù)字只會(huì)從0增長(zhǎng)到1。
這個(gè)是為什么呢?其實(shí)這主要是引用和閉包的區(qū)別。
class 組件里面可以通過(guò) this.state 引用到 count,所以每次 setTimeout 的時(shí)候都能通過(guò)引用拿到上一次的最新 count,所以點(diǎn)擊多少次最后就加了多少。
在 function component 里面每次更新都是重新執(zhí)行當(dāng)前函數(shù),也就是說(shuō) setTimeout 里面讀取到的 count 是通過(guò)閉包獲取的,而這個(gè) count 實(shí)際上只是初始值,并不是上次執(zhí)行完成后的最新值,所以最后只加了1次。
3.3 快照和引用的轉(zhuǎn)換
如果我想讓函數(shù)組件也是從0加到10,那么該怎么來(lái)解決呢?聰明的你一定會(huì)想到,如果模仿類組件里面的 this.state,我們用一個(gè)引用來(lái)保存 count 不就好了嗎?沒錯(cuò),這樣是可以解決,只是這個(gè)引用該怎么寫呢?我在 state 里面設(shè)置一個(gè)對(duì)象好不好?就像下面這樣:
const [state, setState] = useState({ count: 0 })答案是不行,因?yàn)榧词?state 是個(gè)對(duì)象,但每次更新的時(shí)候,要傳一個(gè)新的引用進(jìn)去,這樣的引用依然是沒有意義。
setState({count: state.count + 1})
3.3 useRef
想要解決這個(gè)問(wèn)題,那就涉及到另一個(gè)新的 Hook 方法 —— useRef。useRef 是一個(gè)對(duì)象,它擁有一個(gè) current 屬性,并且不管函數(shù)組件執(zhí)行多少次,而 useRef 返回的對(duì)象永遠(yuǎn)都是原來(lái)那一個(gè)。

useRef 有下面這幾個(gè)特點(diǎn):
useRef是一個(gè)只能用于函數(shù)組件的方法。useRef是除字符串ref、函數(shù)ref、createRef之外的第四種獲取ref的方法。useRef在渲染周期內(nèi)永遠(yuǎn)不會(huì)變,因此可以用來(lái)引用某些數(shù)據(jù)。修改 ref.current不會(huì)引發(fā)組件重新渲染。
useRef vs createRef:
兩者都是獲取 ref 的方式,都有一個(gè) current 屬性。 useRef 只能用于函數(shù)組件,createRef 可以用在類組件中。 useRef 在每次重新渲染后都保持不變,而 createRef 每次都會(huì)發(fā)生變化。
3.4 寫需求遇到的坑

之前在寫需求的時(shí)候遇到過(guò)這樣的一個(gè)坑。bankId 和 ref 都是從接口獲取到的,這里很自然就想到在 useCallback 里面指定依賴。

但是呢,這個(gè) handlerReappear 方法需要在第一次進(jìn)入頁(yè)面的時(shí)候,向 JS Bridge 注冊(cè)的事件,這就導(dǎo)致了一個(gè)問(wèn)題,不管后來(lái) handlerReappear 如何變化,registerHandler 里面依賴的 callback 都是第一次的,這也是閉包導(dǎo)致的問(wèn)題。當(dāng)然,你可能會(huì)說(shuō),我在 useEffect 里面也指定了依賴不好嗎?但要注意這是個(gè)注冊(cè)事件,意味著每次我都要清除上一次的事件,需要調(diào)用到 JS Bridge,在性能上肯定不是個(gè)好辦法。
最終,我選擇使用 useRef 來(lái)保存 bankId 和 ref,這樣就可以通過(guò)引用來(lái)獲取到最新的值。

3.5 Vue3 Composition API

在 vue3 里面提供了新的 Composition API,之前知乎有個(gè)問(wèn)題是 React Hooks 是否可以改為用類似 Vue 3 Composition API 的方式實(shí)現(xiàn)?
然后我寫了一篇文章,利用 Object.defineProperty 簡(jiǎn)單實(shí)現(xiàn)了 Composition API,可以參考:用 React Hooks 簡(jiǎn)單實(shí)現(xiàn) Vue3 Composition API
當(dāng)然這個(gè)實(shí)現(xiàn)還有很多問(wèn)題,也比較簡(jiǎn)單,可以參考工業(yè)聚寫的完整實(shí)現(xiàn):react-use-setup
4. useEffect
useEffect 是一個(gè) Effect Hook,常用于一些副作用的操作,在一定程度上可以充當(dāng) componentDidMount、componentDidUpdate、componentWillUnmount 這三個(gè)生命周期。useEffect 是非常重要的一個(gè)方法,可以說(shuō)是 React Hooks 的靈魂,它用法主要有這么幾種:
useEffect接收兩個(gè)參數(shù),分別是要執(zhí)行的回調(diào)函數(shù)、依賴數(shù)組。如果依賴數(shù)組為空數(shù)組,那么回調(diào)函數(shù)會(huì)在第一次渲染結(jié)束后( componentDidMount)執(zhí)行,返回的函數(shù)會(huì)在組件卸載時(shí)(componentWillUnmount)執(zhí)行。如果不傳依賴數(shù)組,那么回調(diào)函數(shù)會(huì)在每一次渲染結(jié)束后( componentDidMount和componentDidUpdate)執(zhí)行。如果依賴數(shù)組不為空數(shù)組,那么回調(diào)函數(shù)會(huì)在依賴值每次更新渲染結(jié)束后(componentDidUpdate)執(zhí)行,這個(gè)依賴值一般是 state 或者 props。 
useEffect 比較重要,它主要有這幾個(gè)作用:
代替部分生命周期,如 componentDidMount、componentDidUpdate、componentWillUnmount。 更加 reactive,類似 mobx 的 reaction 和 vue 的 watch。 從命令式變成聲明式,不需要再關(guān)注應(yīng)該在哪一步做某些操作,只需要關(guān)注依賴數(shù)據(jù)。 通過(guò) useEffect 和 useState 可以編寫一系列自定義的 Hook。
4.1 useEffect vs useLayoutEffect
useLayoutEffect 也是一個(gè) Hook 方法,從名字上看和 useEffect 差不多,他倆用法也比較像。在90%的場(chǎng)景下我們都會(huì)用 useEffect,然而在某些場(chǎng)景下卻不得不用 useLayoutEffect。useEffect 和 useLayoutEffect 的區(qū)別是:
useEffect 不會(huì) block 瀏覽器渲染,而 useLayoutEffect 會(huì)。 useEffect 會(huì)在瀏覽器渲染結(jié)束后執(zhí)行,useLayoutEffect 則是在 DOM 更新完成后,瀏覽器繪制之前執(zhí)行。
這兩句話該怎么來(lái)理解呢?我們以一個(gè)移動(dòng)的方塊為例子:


在 useEffect 里面會(huì)讓這個(gè)方塊往后移動(dòng) 600px 距離,可以看到這個(gè)方塊在移動(dòng)過(guò)程中會(huì)閃一下。但如果換成了 useLayoutEffect 呢?會(huì)發(fā)現(xiàn)方塊不會(huì)再閃動(dòng),而是直接出現(xiàn)在了 600px 的位置。


原因是 useEffect 是在瀏覽器繪制之后執(zhí)行的,所以方塊一開始就在最左邊,于是我們看到了方塊移動(dòng)的動(dòng)畫。然而 useLayoutEffect 是在繪制之前執(zhí)行的,會(huì)阻塞頁(yè)面的繪制,所以頁(yè)面會(huì)在 useLayoutEffect 里面的代碼執(zhí)行結(jié)束后才去繼續(xù)繪制,于是方塊就直接出現(xiàn)在了右邊。那么這里的代碼是怎么實(shí)現(xiàn)的呢?以 preact 為例,useEffect 在 options.commit 階段執(zhí)行,而 useLayoutEffect 在 options.diffed 階段執(zhí)行。然而在實(shí)現(xiàn) useEffect 的時(shí)候使用了 requestAnimationFrame,requestAnimationFrame 可以控制 useEffect 里面的函數(shù)在瀏覽器重繪結(jié)束,下次繪制之前執(zhí)行。

5. useMemo
useMemo 的用法類似 useEffect,常常用于緩存一些復(fù)雜計(jì)算的結(jié)果。useMemo 接收一個(gè)函數(shù)和依賴數(shù)組,當(dāng)數(shù)組中依賴項(xiàng)變化的時(shí)候,這個(gè)函數(shù)就會(huì)執(zhí)行,返回新的值。
const sum = useMemo(() => {// 一系列計(jì)算}, [count])
舉個(gè)例子會(huì)更加清楚 useMemo 的使用場(chǎng)景,我們就以下面這個(gè) DatePicker 組件的計(jì)算為例:

DatePicker 組件每次打開或者切換月份的時(shí)候,都需要大量的計(jì)算來(lái)算出當(dāng)前需要展示哪些日期。然后再將計(jì)算后的結(jié)果渲染到單元格里面,這里可以使用 useMemo 來(lái)緩存,只有當(dāng)傳入的日期變化時(shí)才去計(jì)算。
6. useCallback
和 useMemo 類似,只不過(guò) useCallback 是用來(lái)緩存函數(shù)。
6.1 匿名函數(shù)導(dǎo)致不必要的渲染
在我們編寫 React 組件的時(shí)候,經(jīng)常會(huì)用到事件處理函數(shù),很多人都會(huì)簡(jiǎn)單粗暴的傳一個(gè)箭頭函數(shù)。
class App extends Component {render() {return{}}>
}}
這種箭頭函數(shù)有個(gè)問(wèn)題,那就是在每一次組件重新渲染的時(shí)候都會(huì)生成一個(gè)重復(fù)的匿名箭頭函數(shù),導(dǎo)致傳給組件的參數(shù)發(fā)生了變化,對(duì)性能造成一定的損耗。
在函數(shù)組件里面,同樣會(huì)有這個(gè)傳遞新的匿名函數(shù)的問(wèn)題。從下面這個(gè)例子來(lái)看,每次點(diǎn)擊 div,就會(huì)引起 Counter 組件重新渲染。這次更新明顯和 Input 組件無(wú)關(guān),但每次重新渲染之后,都會(huì)創(chuàng)建新的 onChange 方法。這樣相當(dāng)于傳給 Input 的 onChange 參數(shù)變化,即使 Input 內(nèi)部做過(guò) shadowEqual 也沒有意義了,都會(huì)跟著重新渲染。原本只想更新 count 值的,可 Input 組件 卻做了不必要的渲染。

這就是體現(xiàn) useCallback 價(jià)值的地方了,我們可以用 useCallback 指定依賴項(xiàng)。在無(wú)關(guān)更新之后,通過(guò) useCallback 取的還是上一次緩存起來(lái)的函數(shù)。因此,useCallback 常常配合 React.memo 來(lái)一起使用,用于進(jìn)行性能優(yōu)化。

7. useReducer && useContext
7.1 useReducer
useReducer 和 useState 的用法很相似,甚至在 preact 中,兩者實(shí)現(xiàn)都是一樣的。useReducer 接收一個(gè) reducer 函數(shù)和初始 state,返回了 state 和 dispatch 函數(shù),常常用于管理一些復(fù)雜的狀態(tài),適合 action 比較多的場(chǎng)景。

7.2 useContext
在上一節(jié)講解 React16 新特性的時(shí)候,我們講過(guò)新版 Context API 的用法。
新版 Context 常常有一個(gè)提供數(shù)據(jù)的生產(chǎn)者(Provider),和一個(gè)消費(fèi)數(shù)據(jù)的消費(fèi)者(Consumer),我們需要通過(guò) Consumer 來(lái)以 render props 的形式獲取到數(shù)據(jù)。如果從祖先組件傳來(lái)了多個(gè) Provider,那最終就又陷入了 render props 嵌套地獄。

useContext 允許我們以扁平化的形式獲取到 Context 數(shù)據(jù)。即使有多個(gè)祖先組件使用多個(gè) Context.Provider 傳值,我們也可以扁平化獲取到每一個(gè) Context 數(shù)據(jù)。

7.3 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 Redux
通過(guò) useReducer 和 useContext,我們完全可以實(shí)現(xiàn)一個(gè)小型的 Redux。
reducer.js

Context.js
export const Context = createContext(null);App.js

8. Custom Hooks
對(duì)于 react 來(lái)說(shuō),在函數(shù)組件中使用 state 固然有一些價(jià)值,但最有價(jià)值的還是可以編寫通用 custom hooks 的能力。想像一下,一個(gè)單純不依賴 UI 的業(yè)務(wù)邏輯 hook,我們開箱即用。不僅可以在不同的項(xiàng)目中復(fù)用,甚至還可以跨平臺(tái)使用,react、react native、react vr 等等。編寫自定義 hook 也需要以 use 開頭,這樣保證可以配合 eslint 插件使用。在 custom hooks 中也可以調(diào)用其他 hook,當(dāng)前的 hook 也可以被其他 hook 或者組件調(diào)用。以官網(wǎng)上這個(gè)獲取好友狀態(tài)的自定義 Hook 為例:

這個(gè)自定義 Hook 里面對(duì)好友的狀態(tài)進(jìn)行了監(jiān)聽,每次狀態(tài)更新的時(shí)候都會(huì)去更新 isOnline,當(dāng)組件卸載的時(shí)候會(huì)清除掉這個(gè)監(jiān)聽。這就是 React Hooks 最有用的地方,它允許我們編寫自定義 Hook,然后這個(gè)自定義 Hook 可以復(fù)用給多個(gè)組件,并且不會(huì)和 UI 耦合到一起。
9. React Hooks 原理
由于 preact hooks 的代碼和原有的邏輯耦合度很小,這里為了更加淺顯易懂,我選用了 preact hooks 的源碼來(lái)解讀。
9.1 Hooks 執(zhí)行流程
在 React 中,組件返回的 JSX 元素也會(huì)被轉(zhuǎn)換為虛擬 DOM,就是下方的 vnode,每個(gè) vnode 上面掛載了一個(gè) _component 屬性,這個(gè)屬性指向了組件實(shí)例。而在組件實(shí)例上面又掛載了一個(gè) _hooks 屬性,這個(gè) _hooks 屬性里面保存了我們執(zhí)行一個(gè)組件的時(shí)候,里面所有 Hook 方法相關(guān)的信息。

首先,我們有一個(gè)全局的 currentIndex 變量,當(dāng)組件第一次渲染或者更新的時(shí)候,它會(huì)在每次進(jìn)入一個(gè)函數(shù)組件的時(shí)候都重置為0,每次遇到一個(gè) Hook 方法就會(huì)增加1,同時(shí)將這個(gè) Hook 方法的信息放到 _list 里面。
當(dāng)我們下次進(jìn)來(lái)或者進(jìn)入下一個(gè)組件的時(shí)候, currentIndex 又會(huì)被置為0。
★組件渲染 => currentIndex 重置 0 => 遇到 Hooks 方法,放進(jìn) _list => currentIndex++ => 渲染結(jié)束
”
★組件更新 => currentIndex 重置 0 => 遇到 Hooks 方法,獲取 _list[currentIndex]=> currentIndex++ => 重復(fù)上面步驟 => 更新結(jié)束
”
這個(gè)時(shí)候就會(huì)從剛才的 _list 里面根據(jù) currentIndex 來(lái)取出對(duì)應(yīng)項(xiàng),所以我們每次進(jìn)來(lái)執(zhí)行 useState,它依然能拿到上一次更新后的值,因?yàn)檫@里是緩存了起來(lái)。

通過(guò)上面的分析,你就不難發(fā)現(xiàn),為什么 hooks 方法不能放在條件語(yǔ)句里面了。因?yàn)槊看芜M(jìn)入這個(gè)函數(shù)的時(shí)候,都是要和 currentIndex 一一匹配的,如果更新前后少了一個(gè) Hook 方法,那么就完全對(duì)不上了,導(dǎo)致出現(xiàn)大問(wèn)題。
9.2 useState 和 useReducer
這樣你再來(lái)看下面 useState 和 useReducer 的源碼就會(huì)更容易理解一些。

很明顯,getHookState 是根據(jù) currentIndex 來(lái)從 _list 里面取和當(dāng)前 Hook 相關(guān)的一些信息。如果是初始化狀態(tài)(即沒有 hookState._component)這個(gè)屬性的時(shí)候,就會(huì)去初始化 useState 的兩個(gè)返回值,否則就會(huì)直接返回上一次緩存的結(jié)果。
9.3 useEffect
useEffect 和 useState 差不多,區(qū)別就在 useEffect 接收的函數(shù)會(huì)放到一個(gè) _pendingEffects 里面,而非 _list 里面。

在 diff 結(jié)束之后會(huì)從 _pendingEffects 里面取出來(lái)函數(shù)一個(gè)個(gè)執(zhí)行。afterPaint 里面使用了 requestAnimateFrame 這個(gè)方法,所以傳給 useEffect 里面的方法是在瀏覽器繪制結(jié)束之后才會(huì)執(zhí)行的。

9.4 總結(jié)
最后,這里對(duì) React Hooks 的整個(gè)運(yùn)行流程來(lái)進(jìn)行一下總結(jié)和梳理。
每個(gè)組件實(shí)例上掛載一個(gè) _hooks 屬性,保證了組件之間不會(huì)影響。 每當(dāng)遇到一個(gè) hooks 方法,就將其 push 到 currentComponent._hooks._list中,且 currentIndex 加一。每次渲染進(jìn)入一個(gè)組件的時(shí)候,都會(huì)從將 currentIndex 重置為 0 。遇到 hooks 方法時(shí),currentIndex 重復(fù)第二步。這樣可以把 currentIndex 和 currentComponent._hooks._list中的對(duì)應(yīng)項(xiàng)匹配起來(lái),直接取上次緩存的值。函數(shù)組件每次重新執(zhí)行后,useState 中還能保持上一次的值,就是來(lái)自于步驟3中的緩存。 由于依賴了 currentComponent 實(shí)例,所以 hooks 不能用于普通函數(shù)中。
10. React Hooks 實(shí)踐
得益于 react hooks 將業(yè)務(wù)邏輯從 ui 中抽離出來(lái),目前社區(qū)里面關(guān)于 react hooks 的實(shí)踐,大都是從功能點(diǎn)出發(fā)。
從最簡(jiǎn)單的 api 封裝,例如 useDebounce、useThrottle、useImmerState 等等,再到業(yè)務(wù)層面功能封裝,比較出名的庫(kù)有 react-use、umijs/hooks 等等。
舉個(gè)栗子:umijs/hooks 的表格:

在后臺(tái)管理系統(tǒng)開發(fā)中,表格是非常常見的場(chǎng)景,將分頁(yè)、查詢、loading、排序等等功能打包封裝成通用 Hook,就能發(fā)揮很大的潛力。

11. 推薦閱讀
Umi Hooks - 助力擁抱 React Hooks 為什么 React 現(xiàn)在要推行函數(shù)式組件,用 class 不好嗎? useRequest- 螞蟻中臺(tái)標(biāo)準(zhǔn)請(qǐng)求 Hooks
最后
如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:
點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)
歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...
關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。

