<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React自定義Hook

          共 6914字,需瀏覽 14分鐘

           ·

          2021-11-01 14:23

          本文適合覺得React自定義hook難、對(duì)自定義hook有興趣的小伙伴閱讀。

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

          一、前言

          React官方文檔:

          https://zh-hans.reactjs.org/docs/hooks-custom.html#gatsby-focus-wrapper

          ? ? hook的兩個(gè)作用:
          1、讓函數(shù)式組件擁有自身的狀態(tài)和生命周期,在沒有使用Class的情況下使用React特性。
          2、讓組件的通用邏輯復(fù)用更簡(jiǎn)單,避免使用HOC來復(fù)用組件的通用邏輯帶來嵌套地獄的問題。邏輯復(fù)用是必不可少的,否則會(huì)導(dǎo)致項(xiàng)目中充斥著大量的重復(fù)代碼。

          二、什么是自定義Hook

          自定義 Hook 是一個(gè)函數(shù),其名稱以 “use” 開頭,函數(shù)內(nèi)部可以調(diào)用其他的 Hook。

          ????以上是React官方的定義,我們可以很明確,自定義一個(gè)Hook就是開發(fā)一個(gè)函數(shù),這個(gè)函數(shù)只是一個(gè)普通的函數(shù),不是什么構(gòu)造函數(shù)、自執(zhí)行函數(shù)之類的。

          那要開發(fā)一個(gè)什么樣的函數(shù)?一個(gè)函數(shù)的組成有函數(shù)名、參數(shù)、函數(shù)內(nèi)部、返回值,我們先結(jié)合React官方文檔中對(duì)自定義Hook的要求,對(duì)其每個(gè)部分分析一下。

          • 函數(shù)名:以?“use” 開頭,比如useState。為什么要以 “use” 開頭。這是因?yàn)椋绻灰?“use” 開頭的話,無法判斷這個(gè)函數(shù)是否包含對(duì)其內(nèi)部 Hook 的調(diào)用,React 將無法自動(dòng)檢查這個(gè)自定義Hook是否違反了 Hook 的規(guī)則。

          • 參數(shù):并沒有特殊要求,只是React官網(wǎng)中有提到過,可以接收另一個(gè)Hook的返回值作為參數(shù),實(shí)現(xiàn)在多個(gè) Hook 之間傳遞信息的功能。

          • 函數(shù)內(nèi)部:在函數(shù)內(nèi)部可以調(diào)用其他的Hook,但是要遵循兩個(gè)規(guī)則:

            • 只在最頂層使用 Hook ,不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook,這是因?yàn)?React 是靠 Hook 調(diào)用的順序來知道哪個(gè) state 對(duì)于哪個(gè)?useState?的。具有看官網(wǎng)這里的解釋。

            • 不要在普通的 JavaScript 函數(shù)中調(diào)用 Hook,只能在 React 函數(shù)中調(diào)用 Hook。這一點(diǎn)很好理解吧,Hook 本身就是由 React 提供的。

          • 返回值:沒有限制一定要返回什么,當(dāng)然一個(gè)函數(shù)默認(rèn)返回一個(gè)undefined

          三、自定義Hook的場(chǎng)景

          ?在開頭提到,在幾個(gè)組件中存在一些通用邏輯,要用自定義Hook的方式將其提取出來,這就是自定義Hook的場(chǎng)景。而且在React官網(wǎng)中也指出:“通過自定義 Hook,可以將組件邏輯提取到可重用的函數(shù)中。”

          ? ? 我們要思考一下什么是組件中的通用邏輯。在Vue工程中,認(rèn)為通用邏輯就是工具類函數(shù),比如深拷貝、防抖函數(shù)、節(jié)流函數(shù)、獲取鏈接指定參數(shù)等等,我把它們提取到util.js中。

          ? ??但是一旦涉及到業(yè)務(wù)方便,比如一段通用邏輯中請(qǐng)求了接口,往往就不進(jìn)行提取了。比如在Vue工程中,把這些通用邏輯提取到混入(mixin)中。

          ? ??然而在React中,我們把跟業(yè)務(wù)相關(guān)的通用邏輯提取為一個(gè)自定義Hook,Hook也是一個(gè)函數(shù),函數(shù)式組件也是一個(gè)函數(shù),在函數(shù)中調(diào)用函數(shù),使用比混入(mixin)更簡(jiǎn)單。當(dāng)然工具類方面的通用邏輯也可以提取為一個(gè)自定義Hook。
          正如React官網(wǎng)所說:“當(dāng)我們想在兩個(gè)函數(shù)之間共享邏輯時(shí),我們會(huì)把它提取到第三個(gè)函數(shù)中。而組件和 Hook 都是函數(shù),所以也同樣適用這種方式。”

          ? ??通用邏輯有很多種,自定義Hook是一個(gè)函數(shù),定義時(shí)要要遵循單一職責(zé)原則。例如把通用邏輯處理的場(chǎng)景進(jìn)一步區(qū)分為UI交互、副作用、生命周期、數(shù)據(jù)處理、DOM處理、優(yōu)化處理等等場(chǎng)景。

          四、自定義一個(gè)最簡(jiǎn)單的Hook

          const?Demo?=?()?=>{
          ??useEffect(()?=>?{
          ?????console.log('組件首次渲染')
          ??},[]);
          }
          export?default?Demo;
          ? ? 以上絕對(duì)是組件中最通用的邏輯。useEffect的第二參數(shù)接收一個(gè)空數(shù)組表示只在組件首次渲染時(shí)執(zhí)行作為第一參數(shù)傳入useEffect的方法。那我們把這個(gè)通用邏輯通過自定義一個(gè)名叫useMount的 Hook 提取出來,實(shí)現(xiàn)如下所示:
          import?{?useEffect?}?from?'react';

          const?useMount?=?(fn:?()?=>?void)?=>?{
          ??useEffect(()?=>?{
          ????fn();
          ??},?[]);
          };

          export?default?useMount;
          useMount這個(gè)自定義Hook接收函數(shù)fn作為參數(shù),在組件首次渲染時(shí)執(zhí)行函數(shù)fn

          五、如何使用自定義Hook

          ? ?自定義Hook是定義在一個(gè)js或ts文件中,最后用export導(dǎo)出,故用import引入使用:
          import?useMount?from?'@/hooks/useMount';

          const?Demo?=?()?=>{
          ??useMount(()?=>{
          ?????console.log('組件首次渲染')
          ??})
          ??return(
          ????<div>demodiv>
          ??)
          }
          export?default?Demo;

          六、自定義Hook的內(nèi)部

          ? ? React提供了10個(gè)內(nèi)部Hook,在自定義Hook的內(nèi)部,一般都是利用這10個(gè)內(nèi)部Hook,進(jìn)行組裝和擴(kuò)展,來自定義各種功能的Hook。

          初學(xué)者在這里往往有個(gè)疑問,比如在一個(gè)自定義Hook?useMyState中使用useState定義了一個(gè)a變量。

          import?{?useState?}?from?'react';

          const?useMyState?=?()?=>?{
          ?const?[a,setA]?=?useState();
          ?return?[a,setA]
          };

          export?default?useMyState;
          然后在使用useMy的組件中又使用useState定義了一個(gè)a變量,這樣會(huì)不會(huì)引起沖突。
          import?useMyState?from?'@/hooks/useMount';

          const?Demo?=?()?=>{
          ??const?[a,setA]?=?useState();
          ??const?[b,setB]?=?useMyState();
          ??return(
          ????<div>demodiv>
          ??)
          }
          export?default?Demo;
          ? ? 完全不要有這樣的擔(dān)憂,兩個(gè)Hook之間是完全隔離的,在一個(gè)組件中可以多次使用useStateuseEffect,它們是完全獨(dú)立的。而且Hook也是個(gè)函數(shù),有函數(shù)作用域在兜底。
          ? ? 疑問解除后,現(xiàn)在如何利用10個(gè)內(nèi)部Hook來自定義新的Hook。舉一個(gè)非常簡(jiǎn)單的例子,我們?cè)陂_發(fā)中要讓一個(gè)值改變之前的值也展示出來。一般會(huì)這么做:
          const?Demo?=?()?=>{
          ??const?[current?,?setCurrent?]?=?useState(1);
          ??const?[previous?,?setPrevious?]=?useState(0);
          ??const?updata?=?()?=>?{
          ????setCurrent(value?=>{
          ??????setPrevious(value);
          ??????return?value+1;
          ????})
          ??}
          ??return?(
          ????<div>
          ??????<div>{current}div>

          ??????<div>{previous}div>
          ??????<div?onClick={updata}>改變div>
          ????div>
          ??);
          }
          export?default?Demo

          其中使用兩個(gè)useState創(chuàng)建了當(dāng)前current和之前previous的兩個(gè)變量,在改變current時(shí),把current之前的值賦值給previous。雖然這么實(shí)現(xiàn)也可以,但是有點(diǎn)不優(yōu)雅,假如還要對(duì)current之前的值進(jìn)行一些判斷再賦值給

          previous,那是不是在改變currentsetCurrent方法中要寫很多不相干的東西,有點(diǎn)違背單一原則。

          這時(shí)候我們就可以自定義一個(gè)Hook來專門處理這種業(yè)務(wù)場(chǎng)景,我把這個(gè)Hook命名為usePrevious
          import?{?useRef?}?from?'react';
          const?usePrevious?=?(state,?compare)?=>{
          ??const?prevRef?=?useRef();
          ??const?curRef?=?useRef();

          ??const?needUpdate?=?typeof?compare?===?'function'???compare(curRef.current,?state)?:?true;
          ??if?(needUpdate)?{
          ????prevRef.current?=?curRef.current;
          ????curRef.current?=?state;
          ??}

          ??return?prevRef.current;
          }

          export?default?usePrevious;
          其中利用useRef來保存一個(gè)值的舊值和新值,比之前用useState來保存好。原因在于useRef返回一個(gè)可變的?ref對(duì)象,其current屬性被初始化為傳入的參數(shù)。返回的ref對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變。
          當(dāng)ref對(duì)象內(nèi)容發(fā)生變化時(shí),useRef并不會(huì)通知你。也就是變更.current屬性不會(huì)引起組件重新渲染, 這點(diǎn)很重要,而useState創(chuàng)建的值只要發(fā)生改變就會(huì)引起組件重新渲染。
          HookusePrevious接受一個(gè)數(shù)據(jù)state和一個(gè)函數(shù)compare,函數(shù)compare接收數(shù)據(jù)的舊值和新值,可以在函數(shù)內(nèi)進(jìn)行判斷后,再?zèng)Q定是否賦值給prevRef.currentHookusePrevious最后返回prevRef.current
          那如何使用usePrevious呢,示例如下所示:
          import?usePrevious?from?'@/hooks/usePrevious';
          const?Demo?=?()?=>{
          ??const?[current?,?setCurrent?]?=?useState(1);
          ??const?compare?=?(oldValue,newValue)?=>{
          ?????if(oldValue?!==?newValue){
          ???????return?true;
          ?????}
          ??}
          ??const?previous?=?usePrevious(current,compare);
          ??const?updata?=?()?=>?{
          ????setCurrent(value?=>{
          ??????value?=?value?+?1;
          ??????return?value;
          ????})
          ??}
          ??return?(
          ????<div>
          ??????<div>{current}div>

          ??????<div>{previous}div>
          ??????<div?onClick={updata}>改變div>
          ????div>
          ??);
          }
          export?default?Demo

          七、自定義Hook和React內(nèi)部Hook有必然關(guān)系嗎

          ? ? 到這里,我們?cè)偎伎家粋€(gè)問題,一定要用React內(nèi)部Hook開發(fā)自定義Hook嗎?

          答案當(dāng)然是不一定了。為什么呢?我們?cè)賮砜匆幌?/span>usePrevious的使用,usePrevious并沒有像useState提供更新值的方法,那為什么usePrevious的返回值previous會(huì)實(shí)時(shí)更新。


          這就要說到函數(shù)式組件的特性了,函數(shù)式組件的函數(shù)體就是類組件中的render(),當(dāng)組件的state或props改變時(shí),組件會(huì)重新渲染,函數(shù)式組件的函數(shù)體會(huì)重新執(zhí)行一遍。


          那么當(dāng)current改變時(shí),Demo這個(gè)函數(shù)會(huì)重新執(zhí)行一遍,執(zhí)行到const previous = usePrevious(current,compare);usePrevious接收到新的current自然返回新的previous,這樣就實(shí)時(shí)更新了。

          好那么現(xiàn)在自定義一個(gè)Hook?useMyCount?如下所示:

          const?useMyCount?=?(state)?=>{
          ??return?state?+?1;
          }

          export?default?useMyCount;
          這種自定義Hook可以嗎?我們使用一下。
          import?useMyCount?from?'@/hooks/usePrevious';
          const?Demo?=?()?=>{
          ??const?[num?,?setNum?]?=?useState(1);
          ??const?count?=?useMyCount(num);
          ??const?updata?=?()?=>?{
          ????setNum(value?=>{
          ??????value?=?value?+?1;
          ??????return?value;
          ????})
          ??}
          ??return?(
          ????<div>
          ??????<div>{num}div>

          ??????<div>{count}div>
          ??????<div?onClick={updata}>加一div>
          ????div>
          ??);
          }
          export?default?Demo

          會(huì)發(fā)現(xiàn)Demo組件中count也會(huì)實(shí)時(shí)更新,而在自定義Hook?useMyCount中,有沒有使用React內(nèi)部Hook沒有絲毫關(guān)系。

          再比如我自定義的一個(gè)獲取當(dāng)前時(shí)間戳的Hook:

          const?useTime?=?()?=>{
          ??return?new?Date().getTime();
          }

          export?default?useTime;
          import?*?as?Api?from?'@/api'
          const?useTask?=?async?()?=>{
          ??const?res?=?await?Api.getTask();
          ??return?res;
          }

          export?default?useTask;

          ? 這些自定義Hook內(nèi)部都沒有用到React的內(nèi)部Hook,所以說自定義Hook不難

          八、關(guān)于自定義Hook一些要求? ?

          ? ? 自定義Hook相當(dāng)寫一個(gè)函數(shù),如果其內(nèi)部要使用React的內(nèi)部Hook,要遵循Hook的使用規(guī)則外。

          此外還要符合書寫函數(shù)的一些要求,比如對(duì)參數(shù)的定義,對(duì)返回值結(jié)構(gòu)的要求。

          ? ? 最后要注意最重要的一點(diǎn),一個(gè)Hook只負(fù)責(zé)一件事情,即遵循單一原則。


          參數(shù)方面的要求

          ? 無參數(shù)

          ? 允許 Hooks 無參數(shù)。


          const?time?=?useTime();

          單參數(shù)

          單參數(shù)無論是否必填直接輸入。

          const?a?=?useA(parame);

          多必選參數(shù)

          必選參數(shù)小于 2 個(gè),應(yīng)平級(jí)輸入。

          const?a?=?useA(parame1,?parame2);

          如果多于 2 個(gè),應(yīng)以 object 形式輸入。

          const?a?=?useA(?{parame1:1,?parame2:2,?parame3:3?}?);

          多非必選參數(shù)

          多非必選參數(shù)以 object 形式輸入。

          const?a?=?useA({parame1?,?parame2?,?parame3?,?parame4?});

          必選參數(shù) + 非必選參數(shù)

          必選參數(shù)在前,非必選參數(shù)在后。

          const?a?=?useA(parame,{parame1?,?parame2?,?parame3?,?parame4?});

          返回值結(jié)構(gòu)的要求

          無輸出

          允許 Hooks 無輸出,一般常見于生命周期類 Hooks。

          useMount(()?=>?{});

          value 型

          Hooks 輸出僅有一個(gè)值。

          const?a?=?useA();

          value setValue 型

          輸出值為 value 和 setValue 類型的,結(jié)構(gòu)為 [value, setValue] 。

          const?[state,?setState]?=?useA(a);

          value actions 型

          其中actions為操作數(shù)據(jù)的方法。

          輸出值為單 value 與多 actions 類型的,結(jié)構(gòu)為 [value, actions] 。

          const?[value,?{?actions1,?actions2,?actions3}]?=?useA(...);

          values 型

          輸出值為多 value 類型的,結(jié)構(gòu)為 {...values}

          const?{value1,?value2,?value3}?=?useA();

          values actions 型

          輸出值為多 value 與多 actions 類型的,結(jié)構(gòu)為 {...values, ...actions} 。

          const?{value1,?value2,?actions1,?actions2}?=?useA(...);

          本文來自作者@紅塵煉心

          https://juejin.cn/post/7022777747722207269

          九、總結(jié)

          ? ? 單純地從自定義Hook來講,其實(shí)并不難。難是在于對(duì)組件中通用邏輯的判別和提取。

          關(guān)注我,一起攜手進(jìn)階

          歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷婷18 | 日韩有码第一页 | 中国AV网| 高清一区二区 | 亚洲无 码A片 |