欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ahooks解決React閉包問題方法示例

 更新時間:2022年07月11日 14:40:59   作者:Gopal  
這篇文章主要為大家介紹了ahooks解決React閉包問題方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

本文是深入淺出 ahooks 源碼系列文章的第三篇,這個系列的目標(biāo)主要有以下幾點(diǎn):

  • 加深對 React hooks 的理解。
  • 學(xué)習(xí)如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。
  • 培養(yǎng)閱讀學(xué)習(xí)源碼的習(xí)慣,工具庫是一個對源碼閱讀不錯的選擇。

注:本系列對 ahooks 的源碼解析是基于 v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見 詳情

系列文章:

本文來探索一下 ahooks 是怎么解決 React 的閉包問題的?。

React 的閉包問題

先來看一個例子:

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>
  );
};

代碼示例

當(dāng)我點(diǎn)擊按鈕的時候,發(fā)現(xiàn) setInterval 中打印出來的值并沒有發(fā)生變化,始終都是 0。這就是 React 的閉包問題。

產(chǎn)生的原因

為了維護(hù) Function Component 的 state,React 用鏈表的方式來存儲 Function Component 里面的 hooks,并為每一個 hooks 創(chuàng)建了一個對象。

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

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

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

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

回到剛剛那個例子:

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

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

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

解決的方法

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

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

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

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

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

終于回到我們 ahooks 主題,基于上述的第二種解決方案,useLatest 這個 hook 隨之誕生。它返回當(dāng)前最新值的 Hook,可以避免閉包問題。實(shí)現(xià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 中另一個場景,是基于 useCallback 的。

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

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

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

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

在這里我們不細(xì)看這個特性,實(shí)際上,在 ahooks 中已經(jīng)實(shí)現(xiàn)了類似的功能,那就是 useMemoizedFn。

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

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ù)的時候,調(diào)用原始的函數(shù)
    memoizedFn.current = function (this, ...args) {
      return fnRef.current.apply(this, args);
    };
  }
  return memoizedFn.current as T;
}

總結(jié)與思考

React 自從引入 hooks,雖然解決了 class 組件的一些弊端,比如邏輯復(fù)用需要通過高階組件層層嵌套等。但是也引入了一些問題,比如閉包問題。

這個是 React 的 Function Component State 管理導(dǎo)致的,有時候會讓開發(fā)者產(chǎn)生疑惑。開發(fā)者可以通過添加依賴或者使用 useRef 的方式進(jìn)行避免。

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

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

參考

以上就是ahooks解決React閉包問題方法示例的詳細(xì)內(nèi)容,更多關(guān)于ahooks React閉包的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 從零搭建react+ts組件庫(封裝antd)的詳細(xì)過程

    從零搭建react+ts組件庫(封裝antd)的詳細(xì)過程

    這篇文章主要介紹了從零搭建react+ts組件庫(封裝antd),實(shí)際上,代碼開發(fā)過程中,還有很多可以輔助開發(fā)的模塊、流程,本文所搭建的整個項(xiàng)目,我都按照文章一步一步進(jìn)行了git提交,開發(fā)小伙伴可以邊閱讀文章邊對照git提交一步一步來看
    2022-05-05
  • React onBlur回調(diào)中使用document.activeElement返回body完美解決方案

    React onBlur回調(diào)中使用document.activeElement返回body完美解決方案

    這篇文章主要介紹了React onBlur回調(diào)中使用document.activeElement返回body完美解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • React Class組件生命周期及執(zhí)行順序

    React Class組件生命周期及執(zhí)行順序

    這篇文章主要介紹了React Class組件生命周期,包括react組件的兩種定義方式和class組件不同階段生命周期函數(shù)執(zhí)行順序,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • create-react-app如何降低react的版本

    create-react-app如何降低react的版本

    這篇文章主要介紹了create-react-app降低react的版本方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React聲明組件的方法總結(jié)

    React聲明組件的方法總結(jié)

    這篇文章主要給大家介紹了react聲明組件有哪幾種方法,各有什么不同,文章通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-11-11
  • React?setState是異步還是同步原理解析

    React?setState是異步還是同步原理解析

    這篇文章主要為大家介紹了React?setState是異步還是同步原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React DOM-diff 節(jié)點(diǎn)源碼解析

    React DOM-diff 節(jié)點(diǎn)源碼解析

    這篇文章主要為大家介紹了React DOM-diff節(jié)點(diǎn)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • React 18版本配置rem 和 vw的詳細(xì)步驟

    React 18版本配置rem 和 vw的詳細(xì)步驟

    這篇文章主要介紹了React 18版本配置rem 和 vw的詳細(xì)步驟,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • React中常用的Hook有哪些

    React中常用的Hook有哪些

    這篇文章主要介紹了react hooks實(shí)現(xiàn)原理,文中給大家介紹了useState dispatch 函數(shù)如何與其使用的 Function Component 進(jìn)行綁定,節(jié)后實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • 關(guān)于react+antd樣式不生效問題的解決方式

    關(guān)于react+antd樣式不生效問題的解決方式

    最近本人在使用Antd開發(fā)時遇到些問題,所以下面這篇文章主要給大家介紹了關(guān)于react+antd樣式不生效問題的解決方式,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07

最新評論