成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

詳解ahooks解決React閉包問題方法

3403771864 / 558人閱讀

  想必大家都能看得懂的源碼 ahooks 整體架構(gòu)篇,且可以使用插件化機(jī)制優(yōu)雅的封裝你的請(qǐng)求hook,現(xiàn)在我們就探討下ahooks 是怎么解決 React 的閉包問題的?。

  React 的閉包問題

  先來看一個(gè)例子:

  import React, { useState, useEffect } from "react";
  export default () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
  setInterval(() => {
  console.log("setInterval:", count);
  }, 1000);
  }, []);
  return (
  <div>
  count: {count}
  <br />
  <button onClick={() => setCount((val) => val + 1)}>增加 1</button>
  </div>
  );
  };

  代碼示例

  點(diǎn)擊按鈕的時(shí)候,發(fā)現(xiàn) setInterval 中打印出來的值并沒有發(fā)生變化,始終都是 0。造成這樣就是因?yàn)?React 的閉包問題。

1.jpg

  產(chǎn)生的原因

  為了維護(hù) Function Component 的 state,React 用鏈表的方式來存儲(chǔ) Function Component 里面的 hooks,并為每一個(gè) hooks 創(chuàng)建了一個(gè)對(duì)象?!?/p>

 type Hook = {
  memoizedState: any,
  baseState: any,
  baseUpdate: Update<any, any> | null,
  queue: UpdateQueue<any, any> | null,
  next: Hook | null,
  };

  這個(gè)對(duì)象的memoizedState屬性就是用來存儲(chǔ)組件上一次更新后的state,next指向下一個(gè) hook 對(duì)象。在組件更新的過程中,hooks 函數(shù)執(zhí)行的順序是不變的,就可以根據(jù)這個(gè)鏈表拿到當(dāng)前 hooks 對(duì)應(yīng)的 Hook 對(duì)象,函數(shù)式組件就是這樣擁有了state的能力。

  伴隨的還有一系列規(guī)則,比如不能將 hooks 寫入到if...else...中。從而保證能夠正確拿到相應(yīng) hook 的 state。

  useEffect 接收了兩個(gè)參數(shù),一個(gè)回調(diào)函數(shù)和一個(gè)數(shù)組。數(shù)組里面就是 useEffect 的依賴,當(dāng)為 [] 的時(shí)候,回調(diào)函數(shù)只會(huì)在組件第一次渲染的時(shí)候執(zhí)行一次。如果有依賴其他項(xiàng),react 會(huì)判斷其依賴是否改變,如果改變了就會(huì)執(zhí)行回調(diào)函數(shù)。

  回到剛剛那個(gè)例子:

  const [count, setCount] = useState(0);
  useEffect(() => {
  setInterval(() => {
  console.log("setInterval:", count);
  }, 1000);
  }, []);

  它第一次執(zhí)行的時(shí)候,執(zhí)行 useState,count 為 0。執(zhí)行 useEffect,執(zhí)行其回調(diào)中的邏輯,啟動(dòng)定時(shí)器,每隔 1s 輸出setInterval: 0。

  須知當(dāng)點(diǎn)擊按鈕使count增加 1 的時(shí)候,整個(gè)函數(shù)式組件重新渲染,這個(gè)時(shí)候前一個(gè)執(zhí)行的鏈表已經(jīng)存在了。useState 將 Hook 對(duì)象 上保存的狀態(tài)置為 1, 那么此時(shí) count 也為 1 了。執(zhí)行 useEffect,其依賴項(xiàng)為空,不執(zhí)行回調(diào)函數(shù)。但是之前的回調(diào)函數(shù)還是在的,它還是會(huì)每隔 1s 執(zhí)行console.log("setInterval:", count);,但這里的 count 是之前第一次執(zhí)行時(shí)候的 count 值,因?yàn)樵诙〞r(shí)器的回調(diào)函數(shù)里面被引用了,形成了閉包一直被保存。

  解決的方法

  解決方法一:給 useEffect 設(shè)置依賴項(xiàng),重新執(zhí)行函數(shù),設(shè)置新的定時(shí)器,拿到最新值。

  // 解決方法一
  useEffect(() => {
  if (timer.current) {
  clearInterval(timer.current);
  }
  timer.current = setInterval(() => {
  console.log("setInterval:", count);
  }, 1000);
  }, [count]);

  解決方法二:使用 useRef。 useRef 返回一個(gè)可變的 ref 對(duì)象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。

  useRef 創(chuàng)建的是一個(gè)普通 Javascript 對(duì)象,而且會(huì)在每次渲染時(shí)返回同一個(gè) ref 對(duì)象,當(dāng)我們變化它的 current 屬性的時(shí)候,對(duì)象的引用都是同一個(gè),所以定時(shí)器中能夠讀到最新的值。

  const lastCount = useRef(count);
  // 解決方法二
  useEffect(() => {
  setInterval(() => {
  console.log("setInterval:", lastCount.current);
  }, 1000);
  }, []);
  return (
  <div>
  count: {count}
  <br />
  <button
  onClick={() => {
  setCount((val) => val + 1);
  // +1
  lastCount.current += 1;
  }}
  >
  增加 1
  </button>
  </div>
  );
  useRef => useLatest

  現(xiàn)在我們來說說我們的主題ahooks 主題,基于上述的第二種解決方案,useLatest 這個(gè) hook 隨之誕生。它返回當(dāng)前最新值的 Hook,可以避免閉包問題。實(shí)現(xiàn)原理很簡(jiǎn)單,只有短短的十行代碼,就是使用 useRef 包一層:

 

 import { useRef } from 'react';
  // 通過 useRef,保持每次獲取到的都是最新的值
  function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref;
  }
  export default useLatest;
  useEvent => useMemoizedFn

  React 中另一個(gè)場(chǎng)景,是基于 useCallback 的。

  const [count, setCount] = useState(0);
  const callbackFn = useCallback(() => {
  console.log(`Current count is ${count}`);
  }, []);

  count 的值變化成多少都不會(huì)影響,執(zhí)行 callbackFn 打印出來的 count 的值始終都是 0。這個(gè)是因?yàn)榛卣{(diào)函數(shù)被 useCallback 緩存,形成閉包,從而形成閉包陷阱。

  這個(gè)問題解決方法?官方提出了 useEvent。它解決的問題:如何同時(shí)保持函數(shù)引用不變與訪問到最新狀態(tài)。使用它之后,上面的例子就變成了。

  const callbackFn = useEvent(() => {
  console.log(`Current count is ${count}`);
  });

  你是否注意啊到,在 ahooks 中已經(jīng)實(shí)現(xiàn)了類似的功能,那就是 useMemoizedFn。

  useMemoizedFn 是持久化 function 的 Hook,理論上,可以使用 useMemoizedFn 完全代替 useCallback。使用 useMemoizedFn,可以省略第二個(gè)參數(shù) deps,同時(shí)保證函數(shù)地址永遠(yuǎn)不會(huì)變化。以上的問題,通過以下的方式就能輕松解決:

 

 const memoizedFn = useMemoizedFn(() => {
  console.log(`Current count is ${count}`);
  });

  Demo 地址

  看看下面代碼,其還是通過 useRef 保持 function 引用地址不變,并且每次執(zhí)行都可以拿到最新的 state 值。

  function useMemoizedFn<T extends noop>(fn: T) {
  // 通過 useRef 保持其引用地址不變,并且值能夠保持值最新
  const fnRef = useRef<T>(fn);
  fnRef.current = useMemo(() => fn, [fn]);
  // 通過 useRef 保持其引用地址不變,并且值能夠保持值最新
  const memoizedFn = useRef<PickFunction<T>>();
  if (!memoizedFn.current) {
  // 返回的持久化函數(shù),調(diào)用該函數(shù)的時(shí)候,調(diào)用原始的函數(shù)
  memoizedFn.current = function (this, ...args) {
  return fnRef.current.apply(this, args);
  };
  }
  return memoizedFn.current as T;
  }

  總結(jié)與思考

  有利有弊,其實(shí)說的就是現(xiàn)在的情況,React 自從引入 hooks,解決了 class 組件的“弊”,但也引入了一些問題,比如閉包問題。

  就是React 的 Function Component State 管理導(dǎo)致的,這也可以讓開發(fā)者可以通過添加依賴或者使用 useRef 的方式進(jìn)行避免。

  ahooks 也意識(shí)到了這個(gè)問題,通過 useLatest 保證獲取到最新的值和 useMemoizedFn 持久化 function 的方式,避免類似的閉包陷阱。

  在多說以下, useMemoizedFn 是 ahooks 輸出函數(shù)的標(biāo)準(zhǔn),因此,所有的輸出函數(shù)都使用useMemoizedFn包一層。另外輸入函數(shù)都使用 useRef 做一次記錄,以保證在任何地方都能訪問到最新的函數(shù)。

       歡迎大家關(guān)注更多精彩內(nèi)容。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/128263.html

相關(guān)文章

  • 解析ahooks整體架構(gòu)及React工具庫源碼

     這是講 ahooks 源碼的第一篇文章,簡(jiǎn)要就是以下幾點(diǎn):  加深對(duì) React hooks 的理解?! W(xué)習(xí)如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫?! ∨囵B(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫是一個(gè)對(duì)源碼閱讀不錯(cuò)的選擇?! ∽ⅲ罕鞠盗袑?duì) ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對(duì)源碼做了一些解讀,可見詳情?! 〉谝黄饕榻B a...

    3403771864 評(píng)論0 收藏0
  • ahooks正式發(fā)布React Hooks工具庫

      起因  社會(huì)在不斷的向前,技術(shù)也在不斷的完善進(jìn)步。從 React Hooks 正式發(fā)布到現(xiàn)在,越來越多的項(xiàng)目正在使用 Function Component 替代 Class Component,Hooks 這一新特性也逐漸被廣泛的使用。 這樣的解析是不是很熟悉,在日常中時(shí)常都有用到,但也有一個(gè)可以解決這樣重復(fù)的就是對(duì)數(shù)據(jù)請(qǐng)求的邏輯處理,對(duì)防抖節(jié)流的邏輯處理等。 另一方面,由于 Hoo...

    3403771864 評(píng)論0 收藏0
  • React官方團(tuán)隊(duì)實(shí)例原生Hook閉包陷阱

      陷進(jìn)到處都是??!本篇文章就說說Hooks使用時(shí)存在所謂的閉包陷阱,看看下面代碼:  functionChat(){   const[text,setText]=useState('');   constonClick=useCallback(()=>{   sendMessage(text);   },[]);   return<SendButtononClick=...

    3403771864 評(píng)論0 收藏0
  • Java與groovy混編 —— 一種兼顧接口清晰和實(shí)現(xiàn)敏捷的開發(fā)方式

    摘要:原文鏈接有大量平均水平左右的工人可被選擇參與進(jìn)來這意味著好招人有成熟的大量的程序庫可供選擇這意味著大多數(shù)項(xiàng)目都是既有程序庫的拼裝,標(biāo)準(zhǔn)化程度高而定制化場(chǎng)景少開發(fā)工具測(cè)試工具問題排查工具完善,成熟基本上沒有團(tuán)隊(duì)愿意在時(shí)間緊任務(wù)重的項(xiàng)目情況 原文鏈接:http://pfmiles.github.io/blog/java-groovy-mixed/ 有大量平均水平左右的工人可被選擇、參與...

    LittleLiByte 評(píng)論0 收藏0
  • 如何用ahooks控制時(shí)機(jī)的hook?

      本篇主要和大家溝通關(guān)于ahooks ,我們可以理解為加深對(duì) React hooks 的了解。  我們先說下關(guān)于抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫?! ∑鋵?shí)我們應(yīng)該培養(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫是一個(gè)對(duì)源碼閱讀不錯(cuò)的選擇。  注:本系列對(duì) ahooks 的源碼解析是基于v3.3.13。  現(xiàn)在就進(jìn)入主題用ahooks 來封裝 React要注意的時(shí)機(jī)?  Fun...

    3403771864 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<