<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官方團(tuán)隊(duì)出手,補(bǔ)齊原生Hook短板

          共 2508字,需瀏覽 6分鐘

           ·

          2022-05-11 12:12

          我們知道,Hooks使用時(shí)存在所謂的「閉包陷阱」,考慮如下代碼:

          function?Chat()?{
          ??const?[text,?setText]?=?useState('');

          ??const?onClick?=?useCallback(()?=>?{
          ????sendMessage(text);
          ??},?[]);

          ??return?<SendButton?onClick={onClick}?/>;
          }

          我們期望點(diǎn)擊后sendMessage能傳遞text的最新值。

          然而實(shí)際上,由于回調(diào)函數(shù)被useCallback緩存,形成閉包,所以點(diǎn)擊的效果始終是sendMessage('')。

          這就是「閉包陷阱」

          以上代碼的一種解決方式是「為useCallback增加依賴項(xiàng)」

          const?onClick?=?useCallback(()?=>?{
          ??sendMessage(text);
          },?[text]);

          但是這么做了后,每當(dāng)依賴項(xiàng)(text)變化,useCallback會(huì)返回一個(gè)全新的onClick引用,這就失去了useCallback「緩存函數(shù)引用」的作用。

          「閉包陷阱」的出現(xiàn),加大了Hooks的上手門檻,也讓開發(fā)者更容易寫出有bug的代碼。

          現(xiàn)在,React官方團(tuán)隊(duì)要出手解決這個(gè)問題。

          useEvent

          解決方式是引入一個(gè)新的原生Hook —— useEvent

          他用于定義一個(gè)函數(shù),這個(gè)函數(shù)有2個(gè)特性:

          1. 在組件多次render時(shí)保持引用一致

          2. 函數(shù)內(nèi)始終能獲取到最新的propsstate

          上面的例子使用useEvent改造后:

          function?Chat()?{
          ??const?[text,?setText]?=?useState('');

          ??const?onClick?=?useEvent(()?=>?{
          ????sendMessage(text);
          ??});

          ??return?<SendButton?onClick={onClick}?/>;
          }

          Chat組件多次render時(shí),onClick始終指向同一個(gè)引用。

          并且onClick觸發(fā)時(shí)始終能獲取到text的最新值。

          之所以叫useEvent,是因?yàn)?code style="font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(145,109,213);font-weight:bolder;">React團(tuán)隊(duì)認(rèn)為這個(gè)Hook的主要應(yīng)用場(chǎng)景是:「封裝事件處理函數(shù)」

          useEvent的實(shí)現(xiàn)

          useEvent的實(shí)現(xiàn)并不困難,代碼類似如下:

          function?useEvent(handler)?{
          ??const?handlerRef?=?useRef(null);

          ??//?視圖渲染完成后更新`handlerRef.current`指向
          ??useLayoutEffect(()?=>?{
          ????handlerRef.current?=?handler;
          ??});

          ??//?用useCallback包裹,使得render時(shí)返回的函數(shù)引用一致
          ??return?useCallback((...args)?=>?{
          ????const?fn?=?handlerRef.current;
          ????return?fn(...args);
          ??},?[]);
          }

          整體包括兩部分:

          1. 返回一個(gè)沒有依賴項(xiàng)的useCallback,使得每次render時(shí)函數(shù)的引用一致
          useCallback((...args)?=>?{
          ??const?fn?=?handlerRef.current;
          ??return?fn(...args);
          },?[]);
          1. 在合適的時(shí)機(jī)更新handlerRef.current,使得實(shí)際執(zhí)行的函數(shù)始終是最新的引用

          與開源Hooks的差異

          很多開源Hooks庫(kù)已經(jīng)實(shí)現(xiàn)類似功能(比如ahooks中的useMemoizedFn

          useEvent與這些開源實(shí)現(xiàn)的差異主要體現(xiàn)在:

          useEvent定位于「處理事件回調(diào)函數(shù)」這一單一場(chǎng)景,而useMemoizedFn定位于「緩存各種函數(shù)」。

          那么問題來了,既然功能類似,那useEvent為什么要限制自己的使用場(chǎng)景呢?

          答案是:為了更穩(wěn)定。

          useEvent能否獲取到最新的stateprops取決于handlerRef.current更新的時(shí)機(jī)。

          在上面模擬實(shí)現(xiàn)中,useEvent更新handlerRef.current的邏輯放在useLayoutEffect回調(diào)中進(jìn)行。

          這就保證了handlerRef.current始終在「視圖完成渲染」后再更新:

          useLayoutEffect(()?=>?{
          ??handlerRef.current?=?handler;
          });

          「事件回調(diào)」觸發(fā)的時(shí)機(jī)顯然在「視圖完成渲染」之后,所以能夠穩(wěn)定獲取到最新的stateprops

          注:源碼內(nèi)的實(shí)際更新時(shí)機(jī)會(huì)更早些,但不影響這里的結(jié)論

          再來看看ahooks中的useMemoizedFn,fnRef.current的更新時(shí)機(jī)是「useMemoizedFn執(zhí)行時(shí)」(即「組件render時(shí)」):

          function?useMemoizedFn<T?extends?noop>(fn:?T)?{
          ??const?fnRef?=?useRef(fn);

          ??//?更新fnRef.current
          ??fnRef.current?=?useMemo(()?=>?fn,?[fn]);

          ??//?...省略代碼
          }

          當(dāng)React18啟用「并發(fā)更新」后,組件render的次數(shù)、時(shí)機(jī)并不確定。

          所以useMemoizedFnfnRef.current的更新時(shí)機(jī)也是不確定的。

          這就增加了在「并發(fā)更新」下使用時(shí)潛在的風(fēng)險(xiǎn)。

          可以說,useEvent通過限制handlerRef.current更新時(shí)機(jī),進(jìn)而限制應(yīng)用場(chǎng)景,最終達(dá)到穩(wěn)定的目的。

          總結(jié)

          useEvent當(dāng)前還處于RFC(Request For Comments)[1]階段。

          很多熱心的開發(fā)者對(duì)這個(gè)Hook的命名提出了建議,比如:useStableCallback

          804590fd682f4172e6ffdce6e96df7e9.webp

          又比如:useLatestClosure

          af4c5c327c127bed4d8abaf0b3dbb737.webp

          從這些命名看,他們顯然擴(kuò)大了useEvent的應(yīng)用場(chǎng)景。

          經(jīng)過本文的分析我們知道,「擴(kuò)大應(yīng)用場(chǎng)景」意味著「增加開發(fā)者使用時(shí)出錯(cuò)的風(fēng)險(xiǎn)」。

          參考資料

          [1]

          RFC(Request For Comments): https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md

          瀏覽 61
          點(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>
                  色欧美片视频在线观看专区 | 那个视频可以看A片 | 久免费视频 | 大香蕉伊人在线看 | 黄色小视频在线 |