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

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

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

正文

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

什么是閉包陷阱?

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

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

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

useState 中的閉包陷阱

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

示例

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

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

避免方法

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

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

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

useEffect 的閉包陷阱

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

示例

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

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

避免方法

為了避免這種閉包陷阱,可以使用 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)。這是因為 class 組件具有實例,可以將狀態(tài)存儲在實例屬性中,以便在組件的生命周期方法和事件處理程序中訪問和更新。

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

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

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

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

當我們在組件內部調用 setCount 函數(shù)時,React 會在內部使用閉包來訪問和更新 count 變量。這是因為,useState 是在組件的頂層作用域中調用的,而 setCount 函數(shù)是在組件的事件處理程序中調用的。這意味著,setCount 函數(shù)需要訪問 count 變量,但是 count 變量無法存儲在實例屬性中。

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

下面是一個例子,展示了 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>
    </>
  );
};

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

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

從 React Hooks 源碼看閉包陷阱

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

例如,在 useState Hook 中,會通過調用 useStateImpl 函數(shù)來獲取當前狀態(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 對象的。它會檢查當前 Fiber 對象上是否已經(jīng)存在對應的 Hook,如果存在的話就直接返回該 Hook,否則就創(chuàng)建一個新的 Hook 對象并存儲到當前 Fiber 對象上:

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

需要注意的是,每個 Hook 對象中都有一個 queue 屬性,用來存儲更新狀態(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();
}

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

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

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 閉包陷阱的詳細內容,更多關于 React Hooks 閉包陷阱的資料請關注腳本之家其它相關文章!

相關文章

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

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

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

    React新文檔切記不要濫用Ref

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

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

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

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

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

    詳解React自定義Hook

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

    react高階組件添加和刪除props

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

    React封裝CustomSelect組件思路詳解

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

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

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

    React實時預覽react-live源碼解析

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

    解決React報錯Property 'X' does not 

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

最新評論