溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用React/Hooks時需要注意過時閉包的示例分析

發(fā)布時間:2021-09-06 15:05:17 來源:億速云 閱讀:149 作者:小新 欄目:web開發(fā)

小編給大家分享一下使用React/Hooks時需要注意過時閉包的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

Hooks 簡化了 React 組件內(nèi)部狀態(tài)和副作用的管理。此外,可以將重復(fù)的邏輯提取到自定義 Hooks 中,以在整個應(yīng)用程序中重復(fù)使用。

Hooks 嚴(yán)重依賴于 JS 閉包。這就是為什么 Hooks 如此具有表現(xiàn)力和簡單,但是閉包有時很棘手。

使用 Hooks 時可能遇到的一個問題就是過時的閉包,這可能很難解決。

讓我們從過時的裝飾開始。然后,看看到過時的閉包如何影響 React Hooks,以及如何解決該問題。

1.過時的閉包

工廠函數(shù) createIncrement(incBy)  返回一個increment和log函數(shù)的元組。調(diào)用時,increment()函數(shù)將內(nèi)部value增加incBy,而log()僅打印一條消息,其中包含有關(guān)當(dāng)前value的信息:

function createIncrement(incBy) {   let value = 0;    function increment() {     value += incBy;     console.log(value);   }   const message = `Current value is ${value}`; function log() { console.log(message); }     return [increment, log]; }  const [increment, log] = createIncrement(1); increment(); //  1 increment(); //  2 increment(); //  3 // 不能正確工作! log();       //  "Current value is 0"

[increment, log] = createIncrement(1)返回一個函數(shù)元組:一個函數(shù)增加內(nèi)部值,另一個函數(shù)記錄當(dāng)前值。

然后,increment()的3次調(diào)用將 value遞增到3。

最后,log()調(diào)用打印消息是 Current value is 0,這有點出乎意料的,因為此時 value 為 3 了。

log()是一個過時的閉包。閉包 log()捕獲了值為 "Current value is 0"的 message變量。

即使 value 變量在調(diào)用increment()時被增加多次,message變量也不會更新,并且總是保持一個過時的值 "Current value is  0"。

過時的閉包捕獲具有過時值的變量。

2.修復(fù)過時的閉包

修復(fù)過時的log()問題需要關(guān)閉實際更改的變量:value的閉包。

我們將語句 const message = ...; 移動到 log() 函數(shù)內(nèi)部:

function createIncrement(incBy) {   let value = 0;    function increment() {     value += incBy;     console.log(value);   }    function log() {  const message = `Current value is ${value}`;    console.log(message);   }      return [increment, log]; }  const [increment, log] = createIncrement(1); increment(); //  1 increment(); //  2 increment(); //  3 // Works! log();       // "Current value is 3"

現(xiàn)在,在調(diào)用了 3 次 increment() 函數(shù)之后,調(diào)用 log() 記錄了實際value:"Current value is 3"。

3. Hooks 中的過時閉包

3.1 useEffect()

我們來看一下使用useEffect() 過時閉包的常見情況。

在組件中,useEffect() 中每2秒記錄一次count的值

function WatchCount() {   const [count, setCount] = useState(0);    useEffect(function() {     setInterval(function log() {       console.log(`Count is: ${count}`);     }, 2000);   }, []);    return (     <div> {count} <button onClick={() => setCount(count + 1) }> Increase </button> </div>   ); }

打開事例(https://codesandbox.io/s/stale-closure-use-effect-broken-2-gyhzk)

并點擊幾次增加按鈕。然后看看控制臺,每2秒出現(xiàn)一次Count is: 0,盡管count狀態(tài)變量實際上已經(jīng)增加了幾次。

使用React/Hooks時需要注意過時閉包的示例分析

為什么會這樣?

第一次渲染時,狀態(tài)變量count初始化為0。

組件安裝后,useEffect()調(diào)用 setInterval(log,  2000)計時器函數(shù),該計時器函數(shù)計劃每2秒調(diào)用一次log()函數(shù)。在這里,閉包log()捕獲到count變量為0。

之后,即使在單擊Increase按鈕時count增加,計時器函數(shù)每2秒調(diào)用一次的log(),使用count的值仍然是0。log()成為一個過時的閉包。

解決方案是讓useEffect()知道閉包log()依賴于count,并在count改變時正確處理間隔的重置

function WatchCount() {   const [count, setCount] = useState(0);    useEffect(function() {     const id = setInterval(function log() {       console.log(`Count is: ${count}`);     }, 2000);     return function() {       clearInterval(id);     }  }, [count]);   return (     <div>  {count}  <button onClick={() => setCount(count + 1) }>  Increase  </button>  </div>   ); }

正確設(shè)置依賴項后,一旦count發(fā)生變化,useEffect()就會更新閉包。

3.2 useState()

組件有1個button ,以1秒延遲異步增加計數(shù)器。

function DelayedCount() {   const [count, setCount] = useState(0);    function handleClickAsync() {     setTimeout(function delay() {       setCount(count + 1);     }, 1000);   }    return (     <div> {count} <button onClick={handleClickAsync}>Increase async</button> </div>   ); }

現(xiàn)在打開演示(https://codesandbox.io/s/use-state-broken-0q994)??焖賳螕?次按鈕。計數(shù)器僅更新為1,而不是預(yù)期的2。

每次單擊setTimeout(delay, 1000)將在1秒后執(zhí)行delay()。delay()此時捕獲到的 count 為 0。

兩個delay()都將狀態(tài)更新為相同的值:setCount(count + 1) = setCount(0 + 1) = setCount(1)。

這是因為第二次單擊的delay()閉包中已捕獲了過時的count變量為0。

為了解決這個問題,我們使用函數(shù)式方法setCount(count => count + 1)來更新count狀態(tài)

function DelayedCount() {   const [count, setCount] = useState(0);    function handleClickAsync() {     setTimeout(function delay() {  setCount(count => count + 1);    }, 1000);   }    function handleClickSync() {     setCount(count + 1);   }    return (     <div>  {count}  <button onClick={handleClickAsync}>Increase async</button>  <button onClick={handleClickSync}>Increase sync</button>  </div>   ); }

打開演示(https://codesandbox.io/s/use-state-fixed-zz78r)。再次快速單擊按鈕2次。計數(shù)器顯示正確的值2。

當(dāng)一個返回基于前一個狀態(tài)的新狀態(tài)的回調(diào)函數(shù)被提供給狀態(tài)更新函數(shù)時,React確保將最新的狀態(tài)值作為該回調(diào)函數(shù)的參數(shù)提供

setCount(alwaysActualStateValue => newStateValue);

這就是為什么在狀態(tài)更新過程中出現(xiàn)的過時裝飾問題可以通過函數(shù)這種方式來解決。

以上是“使用React/Hooks時需要注意過時閉包的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI