成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從根上理解 React Hooks 的閉包陷阱

開發 架構
相信很多用過 hooks 的人都遇到過這個坑,今天我們來思考下 hooks 閉包陷阱的原因和怎么解決。

現在開發 React 組件基本都是用 hooks 了,hooks 很方便,但一不注意也會遇到閉包陷阱的坑。

相信很多用過 hooks 的人都遇到過這個坑,今天我們來思考下 hooks 閉包陷阱的原因和怎么解決。

首先這樣一段代碼,大家覺得有問題沒:

import { useEffect, useState } from 'react';

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {
setInterval(() => {
setCount(count + 1);
}, 500);
}, []);

useEffect(() => {
setInterval(() => {
console.log(count);
}, 500);
}, []);

return <div>guang</div>;
}

export default Dong;

用 useState 創建了個 count 狀態,在一個 useEffect 里定時修改它,另一個 useEffect 里定時打印最新的 count 值。

我們跑一下:

打印的并不是我們預期的 0、1、2、3,而是 0、0、0、0,這是為什么呢?

這就是所謂的閉包陷阱。

首先,我們回顧下 hooks 的原理:hooks 就是在 fiber 節點上存放了 memorizedState 鏈表,每個 hook 都從對應的鏈表元素上存取自己的值。

比如上面 useState、useEffect、useEffect 的 3 個 hook 就對應了鏈表中的 3 個 memorizedState:

然后 hook 是存取各自的那個 memorizedState 來完成自己的邏輯。

hook 鏈表有創建和更新兩個階段,也就是 mount 和 update,第一次走 mount 創建鏈表,后面都走 update。

比如 useEffect 的實現:

特別要注意 deps 參數的處理,如果 deps 為 undefined 就被當作 null 來處理了。

那之后又怎么處理的呢?

會取出新傳入的 deps 和之前存在 memorizedState 的 deps 做對比,如果沒有變,就直接用之前傳入的那個函數,否則才會用新的函數。

deps 對比的邏輯很容易看懂,如果是之前的 deps 是 null,那就返回 false 也就是不相等,否則遍歷數組依次對比:

所以:

如果 useEffect 第二個參數傳入 undefined 或者 null,那每次都會執行。

如果傳入了一個空數組,只會執行一次。

否則會對比數組中的每個元素有沒有改變,來決定是否執行。

這些我們應該比較熟了,但是現在從源碼理清了。

同樣,useMemo、useCallback 等也是同樣的 deps 處理:

理清了 useEffect 等 hook 是在哪里存取數據的,怎么判斷是否執行傳入的函數的之后,再回來看下那個閉包陷阱問題。

我們是這樣寫的:

useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 500);
}, []);

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

deps 傳入了空數組,所以只會執行一次。

對應的源碼實現是這樣的:

如果是需要執行的 effect 會打上 HasEffect 的標記,然后后面會執行:

因為 deps 數組是空數組,所以沒有 HasEffect 的標記,就不會再執行。

我們知道了為什么只執行一次,那只執行一次有什么問題呢?定時器確實只需要設置一次呀?

定時器確實只需要設置一次沒錯,但是在定時器里用到了會變化的 state,這就有問題了:

deps 設置了空數組,那多次 render,只有第一次會執行傳入的函數:

但是 state 是變化的呀,執行的那個函數卻一直引用著最開始的 state。

怎么解決這個問題呢?

每次 state 變了重新創建定時器,用新的 state 變量不就行了:

也就是這樣的:

import { useEffect, useState } from 'react';

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {
setInterval(() => {
setCount(count + 1);
}, 500);
}, [count]);

useEffect(() => {
setInterval(() => {
console.log(count);
}, 500);
}, [count]);

return <div>guang</div>;
}

export default Dong;

這樣每次 count 變了就會執行引用了最新 count 的函數了:

現在確實不是全 0 了,但是這亂七八遭的打印是怎么回事?

那是因為現在確實是執行傳入的 fn 來設置新定時器了,但是之前的那個沒有清楚呀,需要加入一段清除邏輯:

import { useEffect, useState } from 'react';

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1);
}, 500);
return () => clearInterval(timer);
}, [count]);

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

return <div>guang</div>;
}

export default Dong;

加上了 clearInterval,每次執行新的函數之前會把上次設置的定時器清掉。

再試一下:

現在就是符合我們預期的了,打印 0、1、2、3、4。

很多同學學了 useEffect 卻不知道要返回一個清理函數,現在知道為啥了吧。就是為了再次執行的時候清掉上次設置的定時器、事件監聽器等的。

這樣我們就完美解決了 hook 閉包陷阱的問題。

總結

hooks 雖然方便,但是也存在閉包陷阱的問題。

我們過了一下 hooks 的實現原理:

在 fiber 節點的 memorizedState 屬性存放一個鏈表,鏈表節點和 hook 一一對應,每個 hook 都在各自對應的節點上存取數據。

useEffect、useMomo、useCallback 等都有 deps 的參數,實現的時候會對比新舊兩次的 deps,如果變了才會重新執行傳入的函數。所以 undefined、null 每次都會執行,[] 只會執行一次,[state] 在 state 變了才會再次執行。

閉包陷阱產生的原因就是 useEffect 等 hook 里用到了某個 state,但是沒有加到 deps 數組里,這樣導致 state 變了卻沒有執行新傳入的函數,依然引用的之前的 state。

閉包陷阱的解決也很簡單,正確設置 deps 數組就可以了,這樣每次用到的 state 變了就會執行新函數,引用新的 state。不過還要注意要清理下上次的定時器、事件監聽器等。

要理清 hooks 閉包陷阱的原因是要理解 hook 的原理的,什么時候會執行新傳入的函數,什么時候不會。

hooks 的原理確實也不難,就是在 memorizedState 鏈表上的各節點存取數據,完成各自的邏輯的,唯一需要注意的是 deps 數組引發的這個閉包陷阱問題。

責任編輯:武曉燕 來源: 神光的編程秘籍
相關推薦

2022-05-05 08:31:48

useRefuseEffecthook

2024-01-08 08:35:28

閉包陷阱ReactHooks

2021-02-24 07:40:38

React Hooks閉包

2021-05-11 08:48:23

React Hooks前端

2022-08-21 09:41:42

ReactVue3前端

2016-10-27 19:26:47

Javascript閉包

2016-09-18 20:53:16

JavaScript閉包前端

2022-10-24 08:08:27

閉包編譯器

2011-03-02 12:33:00

JavaScript

2019-08-20 15:16:26

Reacthooks前端

2017-05-22 16:08:30

前端開發javascript閉包

2022-06-08 08:01:20

useEffect數組函數

2023-11-06 08:00:00

ReactJavaScript開發

2020-07-29 10:10:37

HTTP緩存前端

2020-10-28 09:12:48

React架構Hooks

2020-04-27 09:40:13

Reacthooks前端

2021-03-18 08:00:55

組件Hooks React

2010-07-26 11:27:58

Perl閉包

2022-05-06 16:18:00

Block和 C++OC 類lambda

2022-03-31 17:54:29

ReactHooks前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人激情视频在线 | 久久com| 黄网站在线播放 | 91视频久久 | 成人精品一区二区三区中文字幕 | 国产精品视频中文字幕 | av天天干| 久久久www成人免费无遮挡大片 | 一区视频 | 欧美狠狠操 | 国产精品久久av | 视频在线一区二区 | 久久久久国产 | 亚洲精品久久久久国产 | 欧美精品在线一区 | 中文字幕一区二区三区在线乱码 | 国产伦一区二区三区四区 | 精品久久久久久久久久久 | 欧美三级三级三级爽爽爽 | 日本精品视频一区二区三区四区 | 九九热免费看 | 亚洲不卡 | 久久一区二区视频 | 亚洲综合成人网 | 亚洲欧美在线视频 | 亚洲电影第三页 | 男人天堂网av | 欧美日韩不卡在线 | 日本免费一区二区三区四区 | 97超碰人人草 | 超碰97人人人人人蜜桃 | 国产精品1区 | 一区二区伦理电影 | 美女在线观看av | 国产亚洲欧美在线视频 | 久草视频观看 | 黄色大片免费播放 | 国产不卡一 | 欧美视频三级 | 久久久国产精品视频 | 青青久久|