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

深入解析React?Hooks?閉包陷阱

 更新時(shí)間:2023年05月09日 08:43:39   作者:晚天  
這篇文章主要為大家介紹了React Hooks閉包陷阱的深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

React Hooks 是 React 16.8 版本引入的一種新的特性,它允許我們在不編寫 class 組件的情況下使用 state 以及其他的 React 功能。其中,最為常用的就是 useState 和 useEffect。在使用 React Hooks 時(shí),由于函數(shù)組件沒有實(shí)例,所以 Hooks 靠的是閉包來訪問和更新 state。但是,在使用 Hooks 時(shí),我們需要注意閉包陷阱問題。

什么是閉包陷阱?

閉包是指一個(gè)函數(shù)可以訪問定義在函數(shù)外部的變量。在 React 中,Hooks 函數(shù)也是閉包,它們可以訪問定義在函數(shù)外部的變量。React Hooks 的閉包陷阱與普通 JavaScript 中的閉包陷阱類似,但是由于 React Hooks 的設(shè)計(jì),使用 Hooks 時(shí)可能會(huì)遇到一些特定的問題。

React Hooks 中的閉包陷阱主要會(huì)發(fā)生在兩種情況:

  • 在 useState 中使用閉包;
  • 在 useEffect 中使用閉包。

useState 中的閉包陷阱

在useState中使用閉包,主要是因?yàn)閡seState的參數(shù)只會(huì)在組件掛載時(shí)執(zhí)行一次。如果我們在useState中使用閉包,那么閉包中的變量值會(huì)被緩存,這意味著當(dāng)我們在組件中更新狀態(tài)時(shí),閉包中的變量值不會(huì)隨之更新。

示例

React Hooks 的閉包陷阱發(fā)生在 useState 鉤子函數(shù)中的示例,如下:

function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setTimeout(() => {
      setCount(count + 1);
    }, 1000);
  };
  const handleReset = () => {
    setCount(0);
  };
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
      <button onClick={handleReset}>Reset</button>
    </div>
  );
}

在上面的代碼中,我們定義了一個(gè)handleClick函數(shù),它使用了一個(gè)閉包來緩存count的值。然而,由于閉包中的count值被緩存了,這意味著即使我們在1秒后調(diào)用setCount方法來更新count的值,閉包中的count值仍然是舊的值。因此,如果我們點(diǎn)擊Increment按鈕,即使我們重復(fù)點(diǎn)擊多次,計(jì)數(shù)器也只會(huì)增加1次。

避免方法

為了解決這個(gè)問題,我們需要使用React Hooks提供的更新函數(shù)的形式來更新狀態(tài)。我們可以把handleClick函數(shù)改成這樣:

const handleClick = () => {
  setTimeout(() => {
    setCount(count => count + 1);
  }, 1000);
};

在這個(gè)版本的handleClick函數(shù)中,我們使用了setCount的更新函數(shù)形式。這個(gè)函數(shù)會(huì)接收count的當(dāng)前值作為參數(shù),這樣我們就可以在閉包中使用這個(gè)值,而不需要擔(dān)心它被緩存。

useEffect 的閉包陷阱

在useEffect中使用閉包的問題則是因?yàn)閡seEffect中的函數(shù)是在每次組件更新時(shí)都會(huì)執(zhí)行一次。如果我們在useEffect中使用閉包,那么這個(gè)閉包中的變量值也會(huì)被緩存,這樣就可能會(huì)導(dǎo)致一些問題。

示例

React Hooks 中的閉包陷阱通常發(fā)生在 useEffect 鉤子函數(shù)中,例如:

function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => {
      console.log(count);
    }, 1000);
    return () => clearInterval(timer);
  }, []);
  const handleClick = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

在這個(gè)例子中,我們使用了 useState 和 useEffect Hooks。在 useEffect 回調(diào)函數(shù)內(nèi)部,我們使用了一個(gè) setTimeout 函數(shù)來更新 count 狀態(tài)變量。然而,由于 useEffect 只會(huì)在組件首次渲染時(shí)執(zhí)行一次,因此閉包中的 count 變量始終是首次渲染時(shí)的變量,而不是最新的值。

避免方法

為了避免這種閉包陷阱,可以使用 useEffect Hook 來更新狀態(tài)。例如,以下代碼中,通過 useEffect Hook 來更新 count 的值,就可以避免閉包陷阱:

useEffect(() => {
  const timer = setInterval(() => {
    console.log(count);
  }, 1000);
  return () => clearInterval(timer);
}, [count]);

通過閉包訪問和更新 state

在 React 中,class 組件可以使用 this.state 和 this.setState 來管理組件的狀態(tài)。這是因?yàn)?class 組件具有實(shí)例,可以將狀態(tài)存儲(chǔ)在實(shí)例屬性中,以便在組件的生命周期方法和事件處理程序中訪問和更新。

而函數(shù)組件則沒有實(shí)例,無法將狀態(tài)存儲(chǔ)在實(shí)例屬性中。為了解決這個(gè)問題,React 引入了 React Hooks,其中最為常用的是 useState。useState 允許我們在函數(shù)組件中使用 state,而無需編寫 class 組件。

useState 是通過閉包來實(shí)現(xiàn)的。當(dāng)我們調(diào)用 useState 時(shí),它會(huì)返回一個(gè)數(shù)組,其中第一個(gè)元素是當(dāng)前狀態(tài)的值,第二個(gè)元素是更新狀態(tài)的函數(shù)。例如:

import React, { useState } from 'react';
const Counter = () => {
  const [count, setCount] = useState(0);
  // ...
};

在這個(gè)例子中,useState 的初始值為 0,useState 的返回值是一個(gè)數(shù)組 [count, setCount],其中 count 是當(dāng)前狀態(tài)的值,setCount 是更新狀態(tài)的函數(shù)。

當(dāng)我們在組件內(nèi)部調(diào)用 setCount 函數(shù)時(shí),React 會(huì)在內(nèi)部使用閉包來訪問和更新 count 變量。這是因?yàn)?,useState 是在組件的頂層作用域中調(diào)用的,而 setCount 函數(shù)是在組件的事件處理程序中調(diào)用的。這意味著,setCount 函數(shù)需要訪問 count 變量,但是 count 變量無法存儲(chǔ)在實(shí)例屬性中。

為了解決這個(gè)問題,React 使用了閉包,將 count 變量保存在內(nèi)部函數(shù)中。當(dāng)組件重新渲染時(shí),React 會(huì)創(chuàng)建一個(gè)新的閉包,并將 count 變量的值更新為新的狀態(tài)值。這個(gè)新的閉包會(huì)在下一次調(diào)用 setCount 函數(shù)時(shí)被使用。

下面是一個(gè)例子,展示了 useState 如何通過閉包來訪問和更新 state 的:

import React, { useState } from 'react';
const Counter = () => {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount(count + 1);
  };
  return (
    <>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>Click me</button>
    </>
  );
};

在這個(gè)例子中,我們調(diào)用 useState,并將初始值設(shè)置為 0。在組件內(nèi)部,我們創(chuàng)建了一個(gè) handleClick 函數(shù),并調(diào)用 setCount 函數(shù)來更新 count 的值。由于 setCount 函數(shù)是在 handleClick 函數(shù)中調(diào)用的,因此需要使用閉包來訪問和更新 count 變量。

需要注意的是,由于閉包的作用,如果我們在組件的事件處理程序中訪問了過時(shí)的 state,可能會(huì)導(dǎo)致組件的狀態(tài)出現(xiàn)錯(cuò)誤。為了避免這種情況,我們需要使用 React Hooks 提供的其他功能,例如 useEffect 和 useCallback。這些功能可以幫助我們避免閉包陷阱,確保組件的狀態(tài)更新正確地渲染到視圖上。

從 React Hooks 源碼看閉包陷阱

React Hooks 中閉包陷阱的問題源于 useState 等 Hooks 的實(shí)現(xiàn)方式。在 React 內(nèi)部,每個(gè)組件都有一個(gè)對(duì)應(yīng)的 Fiber 對(duì)象,表示組件的渲染狀態(tài)。useState 等 Hooks 的實(shí)現(xiàn)都是基于這個(gè) Fiber 對(duì)象的,并且會(huì)在 Fiber 對(duì)象中存儲(chǔ)當(dāng)前狀態(tài)值和更新狀態(tài)的函數(shù)。

例如,在 useState Hook 中,會(huì)通過調(diào)用 useStateImpl 函數(shù)來獲取當(dāng)前狀態(tài)值和更新狀態(tài)的函數(shù):

function useState(initialState) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

function useStateImpl(initialState) {
  const hook = mountState(initialState);
  return [hook.memoizedState, dispatchAction.bind(null, hook.queue)];
}

其中,mountState 函數(shù)是用來初始化 Hook 對(duì)象的。它會(huì)檢查當(dāng)前 Fiber 對(duì)象上是否已經(jīng)存在對(duì)應(yīng)的 Hook,如果存在的話就直接返回該 Hook,否則就創(chuàng)建一個(gè)新的 Hook 對(duì)象并存儲(chǔ)到當(dāng)前 Fiber 對(duì)象上:

function mountState(initialState) {
  const currentHook = updateQueue.next;
  if (currentHook !== null) {
    updateQueue.next = currentHook.next;
    return currentHook;
  } else {
    const newHook = {
      memoizedState: typeof initialState === 'function' ? initialState() : initialState,
      queue: [],
      next: null,
    };
    if (updateQueue.last === null) {
      updateQueue.first = updateQueue.last = newHook;
    } else {
      updateQueue.last = updateQueue.last.next = newHook;
    }
    return newHook;
  }
}

需要注意的是,每個(gè) Hook 對(duì)象中都有一個(gè) queue 屬性,用來存儲(chǔ)更新狀態(tài)的 action。而 dispatchAction 函數(shù)則是用來觸發(fā)更新的:

function dispatchAction(queue, action) {
  const update = {
    action,
    next: null,
  };
  if (queue.last === null) {
    queue.first = queue.last = update;
  } else {
    queue.last = queue.last.next = update;
  }
  scheduleWork();
}

在組件重新渲染時(shí),React 會(huì)重新執(zhí)行函數(shù)組件的函數(shù)體,從而調(diào)用 useState 等 Hook 重新獲取狀態(tài)值和更新狀態(tài)的函數(shù)。由于每次重新渲染都會(huì)創(chuàng)建一個(gè)新的 Fiber 對(duì)象,因此在新的 Fiber 對(duì)象上獲取到的 Hook 對(duì)象和狀態(tài)值都是新的。

然而,由于更新狀態(tài)的函數(shù)是存儲(chǔ)在 Hook 對(duì)象中的,因此會(huì)造成更新函數(shù)的閉包引用的是舊的狀態(tài)值,而不是最新的狀態(tài)值。例如,在以下代碼中,每次點(diǎn)擊按鈕都會(huì)增加 count 的值,但是打印出來的 count 值卻始終為 1,這是因?yàn)?setCount 使用的是 count 的初始值,而不是最新的值,因?yàn)?setCount 是在一個(gè)閉包中定義的:

function Counter() {
  let count = 0;
  const [visible, setVisible] = useState(false);
  function handleClick() {
    count++;
    console.log(count);
    setVisible(!visible);
  }
  return (
    <>
      <button onClick={handleClick}>Click me</button>
      {visible && <div>Count: {count}</div>}
    </>
  );
}

以上就是 React Hooks 閉包陷阱的詳細(xì)內(nèi)容,更多關(guān)于 React Hooks 閉包陷阱的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React父子組件間的傳值的方法

    React父子組件間的傳值的方法

    在單頁面里面,父子組件傳值是比較常見的,這篇文章主要介紹了React父子組件間的傳值的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • React新文檔切記不要濫用Ref

    React新文檔切記不要濫用Ref

    這篇文章主要為大家介紹了React新文檔濫用Ref出現(xiàn)的問題詳解,以及如何正確的使用Ref,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-07-07
  • React?Virtual?DOM前端框架全面分析

    React?Virtual?DOM前端框架全面分析

    這篇文章主要為大家介紹了React?Virtual?DOM前端框架全面分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 詳解React?Native中如何使用自定義的引用路徑

    詳解React?Native中如何使用自定義的引用路徑

    這篇文章主要為大家介紹了React?Native中如何使用自定義的引用路徑詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 詳解React自定義Hook

    詳解React自定義Hook

    在React項(xiàng)目中,我們經(jīng)常會(huì)使用到React自帶的幾個(gè)內(nèi)置Hooks,如 useState,useContext和useEffect。雖然在React中找不到這些 Hooks,但React提供了非常靈活的方式讓你為自己的需求來創(chuàng)建自己的自定義Hooks,想了解更多的,歡迎閱讀本文
    2023-04-04
  • react高階組件添加和刪除props

    react高階組件添加和刪除props

    這篇文章主要介紹了react高階組件添加和刪除props,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • React封裝CustomSelect組件思路詳解

    React封裝CustomSelect組件思路詳解

    小編需要封裝一個(gè)通過Popover彈出框里可以自定義渲染內(nèi)容的組件,渲染內(nèi)容暫時(shí)有: 單選框, 復(fù)選框,接下來通過本文給大家分享React封裝CustomSelect組件思路,需要的朋友可以參考下
    2022-07-07
  • 如何在React?Native開發(fā)中防止滑動(dòng)過程中的誤觸

    如何在React?Native開發(fā)中防止滑動(dòng)過程中的誤觸

    在使用React?Native開發(fā)的時(shí),當(dāng)我們快速滑動(dòng)應(yīng)用的時(shí)候,可能會(huì)出現(xiàn)誤觸,導(dǎo)致我們會(huì)點(diǎn)擊到頁面中的某一些點(diǎn)擊事件,誤觸導(dǎo)致頁面元素響應(yīng)從而進(jìn)行其他操作,表現(xiàn)出非常不好的用戶體驗(yàn)。
    2023-05-05
  • React實(shí)時(shí)預(yù)覽react-live源碼解析

    React實(shí)時(shí)預(yù)覽react-live源碼解析

    這篇文章主要為大家介紹了React實(shí)時(shí)預(yù)覽react-live源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 解決React報(bào)錯(cuò)Property 'X' does not exist on type 'HTMLElement'

    解決React報(bào)錯(cuò)Property 'X' does not 

    這篇文章主要為大家介紹了解決React報(bào)錯(cuò)Property 'X' does not exist on type 'HTMLElement',有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評(píng)論