1. React Hooks 原理與最佳實(shí)踐

        共 9643字,需瀏覽 20分鐘

         ·

        2020-11-22 02:47

        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):

        1. Loading 是否可以復(fù)用?
        2. 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è)特性。

        1. 多個(gè)狀態(tài)不會(huì)產(chǎn)生嵌套,寫法還是平鋪的

        2. 允許函數(shù)組件使用 state 和部分生命周期

        3. 更容易將組件的 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,它的用法主要有這幾種:

        1. useState 接收一個(gè)初始值,返回一個(gè)數(shù)組,數(shù)組里面分別是當(dāng)前值和修改這個(gè)值的方法(類似 state 和 setState)。
        2. useState 接收一個(gè)函數(shù),返回一個(gè)數(shù)組。
        3. 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 還是有一些差異:

        1. function state 的粒度更細(xì),class state 過(guò)于無(wú)腦。
        2. function state 保存的是快照,class state 保存的是最新值。
        3. 引用類型的情況下,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):

        1. useRef 是一個(gè)只能用于函數(shù)組件的方法。
        2. useRef 是除字符串 ref、函數(shù) refcreateRef 之外的第四種獲取 ref 的方法。
        3. useRef 在渲染周期內(nèi)永遠(yuǎn)不會(huì)變,因此可以用來(lái)引用某些數(shù)據(jù)。
        4. 修改 ref.current 不會(huì)引發(fā)組件重新渲染。

        useRef vs createRef:

        1. 兩者都是獲取 ref 的方式,都有一個(gè) current 屬性。
        2. useRef 只能用于函數(shù)組件,createRef 可以用在類組件中。
        3. useRef 在每次重新渲染后都保持不變,而 createRef 每次都會(huì)發(fā)生變化。

        3.4 寫需求遇到的坑

        之前在寫需求的時(shí)候遇到過(guò)這樣的一個(gè)坑。bankIdref 都是從接口獲取到的,這里很自然就想到在 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)保存 bankIdref,這樣就可以通過(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) componentDidMountcomponentDidUpdate、componentWillUnmount 這三個(gè)生命周期。useEffect 是非常重要的一個(gè)方法,可以說(shuō)是 React Hooks 的靈魂,它用法主要有這么幾種:

        1. useEffect 接收兩個(gè)參數(shù),分別是要執(zhí)行的回調(diào)函數(shù)、依賴數(shù)組。
        2. 如果依賴數(shù)組為空數(shù)組,那么回調(diào)函數(shù)會(huì)在第一次渲染結(jié)束后(componentDidMount)執(zhí)行,返回的函數(shù)會(huì)在組件卸載時(shí)(componentWillUnmount)執(zhí)行。
        3. 如果不傳依賴數(shù)組,那么回調(diào)函數(shù)會(huì)在每一次渲染結(jié)束后(componentDidMountcomponentDidUpdate)執(zhí)行。
        4. 如果依賴數(shù)組不為空數(shù)組,那么回調(diào)函數(shù)會(huì)在依賴值每次更新渲染結(jié)束后(componentDidUpdate)執(zhí)行,這個(gè)依賴值一般是 state 或者 props。

        useEffect 比較重要,它主要有這幾個(gè)作用:

        1. 代替部分生命周期,如 componentDidMount、componentDidUpdate、componentWillUnmount。
        2. 更加 reactive,類似 mobx 的 reaction 和 vue 的 watch。
        3. 從命令式變成聲明式,不需要再關(guān)注應(yīng)該在哪一步做某些操作,只需要關(guān)注依賴數(shù)據(jù)。
        4. 通過(guò) useEffect 和 useState 可以編寫一系列自定義的 Hook。

        4.1 useEffect vs useLayoutEffect

        useLayoutEffect 也是一個(gè) Hook 方法,從名字上看和 useEffect 差不多,他倆用法也比較像。在90%的場(chǎng)景下我們都會(huì)用 useEffect,然而在某些場(chǎng)景下卻不得不用 useLayoutEffect。useEffect 和 useLayoutEffect 的區(qū)別是:

        1. useEffect 不會(huì) block 瀏覽器渲染,而 useLayoutEffect 會(huì)。
        2. 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é)和梳理。

        1. 每個(gè)組件實(shí)例上掛載一個(gè) _hooks 屬性,保證了組件之間不會(huì)影響。
        2. 每當(dāng)遇到一個(gè) hooks 方法,就將其 push 到 currentComponent._hooks._list 中,且 currentIndex 加一。
        3. 每次渲染進(jìn)入一個(gè)組件的時(shí)候,都會(huì)從將 currentIndex 重置為 0 。遇到 hooks 方法時(shí),currentIndex 重復(fù)第二步。這樣可以把 currentIndex 和 currentComponent._hooks._list 中的對(duì)應(yīng)項(xiàng)匹配起來(lái),直接取上次緩存的值。
        4. 函數(shù)組件每次重新執(zhí)行后,useState 中還能保持上一次的值,就是來(lái)自于步驟3中的緩存。
        5. 由于依賴了 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. 推薦閱讀

        1. Umi Hooks - 助力擁抱 React Hooks
        2. 為什么 React 現(xiàn)在要推行函數(shù)式組件,用 class 不好嗎?
        3. useRequest- 螞蟻中臺(tái)標(biāo)準(zhǔn)請(qǐng)求 Hooks

        最后



        如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

        1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

        2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

        3. 關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。


        點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了


        瀏覽 56
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. www97色色 | 攵女乱h系列合集多女国产剧 | 婷婷久久丁香 | 欧美亚洲精品 99欧美久久久 | 精品A片九九九九免费视频 |