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

從根上理解 React Hooks 的閉包陷阱(續集)

開發 前端
useRef 能解決閉包陷阱的原因是 useEffect 等 hook 里不直接引用 state,而是引用 ref.current,這樣后面只要修改了 ref 中的值,這里取出來的就是最新的。

??上篇文章??我們知道了什么是 hooks 的閉包陷阱,它的產生原因和解決方式,并通過一個案例做了演示。

其實那個案例的閉包陷阱的解決方式不夠完善,這篇文章我們再完善一下。

首先我們先來回顧下什么是閉包陷阱:

hooks 的閉包陷阱是指 useEffect 等 hook 中用到了某個 state,但是沒有把它加到 deps 數組里,導致 state 變了,但是執行的函數依然引用著之前的 state。

它的解決方式就是正確設置 deps 數組,把用到的 state 放到 deps 數組里,這樣每次 state 變了就能執行最新的函數,引用新的 state。同時要清理上次的定時器、事件監聽器等。

我們舉了這樣一個例子:

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;

每次打印都是 0 :

解決方式就是把 count 設置到 deps 里,并添加清理函數:

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;

這樣就能解決閉包陷阱:

但是這種解決閉包陷阱的方式用在定時器上不是很合適。

為什么呢?

因為現在每次 count 變了就會重置定時器,那之前的計時就重新計算,這樣就會導致計時不準。

所以,這種把依賴的 state 添加到 deps 里的方式是能解決閉包陷阱,但是定時器不能這樣做。

那還有什么方式能解決閉包陷阱呢?

useRef。

閉包陷阱產生的原因就是 useEffect 的函數里引用了某個 state,形成了閉包,那不直接引用不就行了?

useRef 是在 memorizedState 鏈表中放一個對象,current 保存某個值。

它的源碼是這樣的:

初始化的時候創建了一個對象放在 memorizedState 上,后面始終返回這個對象。

這樣通過 useRef 保存回調函數,然后在 useEffect 里從 ref.current 來取函數再調用,避免了直接調用,也就沒有閉包陷阱的問題了。

也就是這樣:

const fn = () => {
console.log(count);
};
const ref = useRef(fn);
useLayoutEffect(() => {
ref.current = fn;
});
useEffect(() => {
setInterval(() => ref.current(), 500);
}, []);

useEffect 里執行定時器,deps 設置為了 [],所以只會執行一次,回調函數用的是 ref.current,沒有直接依賴某個 state,所以不會有閉包陷阱。

用 useRef 創建個 ref 對象,初始值為打印 count 的回調函數,每次 render 都修改下其中的函數為新創建的函數,這個函數里引用的 count 就是最新的。

這里用了 useLayoutEffect 而不是 useEffect 是因為 useLayoutEffect 是在 render 前同步執行的,useEffect 是在 render 后異步執行的,所以用 useLayoutEffect 能保證在 useEffect 之前被調用。

這種方式避免了 useEffect 里直接對 state 的引用,從而避免了閉包問題。

另外,修改 count 的地方,可以用 setCount(count => count + 1) 代替 setCount(count + 1),這樣也就避免了閉包問題:

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

現在組件的代碼是這樣的:

import { useEffect, useLayoutEffect, useState, useRef } from 'react';
function Dong() {
const [count, setCount] = useState(0);
useEffect(() => {
setInterval(() => {
setCount(count => count + 1);
}, 500);
}, []);
const fn = () => {
console.log(count);
};
const ref = useRef(fn);
useLayoutEffect(() => {
ref.current = fn;
});
useEffect(() => {
setInterval(() => ref.current(), 500);
}, []);
return <div>guang</div>;
}
export default Dong;

測試下:

確實,打印也是正常的,這就是解決閉包陷阱的第二種方式,通過 useRef 避免直接對 state 的引用,從而避免閉包問題。

這段邏輯用到了多個 hook,可以封裝成個自定義 hook:

function useInterval(fn, time) {
const ref = useRef(fn);
useLayoutEffect(() => {
ref.current = fn;
});
useEffect(() => {
setInterval(() => ref.current(), time);
}, []);
}

然后組件代碼就可以簡化了:

function Dong() {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 500);
useInterval(() => {
console.log(count);
}, 500);
return <div>guang</div>;
}

這樣我們就用 useRef 的方式解決了閉包陷阱問題。

總結

上篇文章我們通過把依賴的 state 添加到 deps 數組中的方式,使得每次 state 變了就執行新的函數,引用新的 state,從而解決了閉包陷阱問題。

這種方式用在定時器上是不合適的,因為定時器一旦被重置和重新計時,那計時就不準確了。

所以我們才用了避免閉包陷阱的第二種方式:使用 useRef。

useRef 能解決閉包陷阱的原因是 useEffect 等 hook 里不直接引用 state,而是引用 ref.current,這樣后面只要修改了 ref 中的值,這里取出來的就是最新的。

然后我們把這段邏輯封裝成了個自定義 hook,這樣可以方便復用。

解決 hooks 的閉包陷阱有兩種方式:

  • 設置依賴的 state 到 deps 數組中并添加清理函數。
  • 不直接引用 state,把 state 放到 useRef 創建的 ref 對象中再引用。

處理定時器的時候,為保證計時的準確,最好使用 useRef 的方式,其余情況兩種都可以。

責任編輯:姜華 來源: 神光的編程秘籍
相關推薦

2022-05-04 10:38:58

React閉包組件

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前端

2022-06-08 08:01:20

useEffect數組函數

2023-11-06 08:00:00

ReactJavaScript開發

2017-05-22 16:08:30

前端開發javascript閉包

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

2022-05-06 16:18:00

Block和 C++OC 類lambda

2022-03-31 17:54:29

ReactHooks前端

2010-07-26 11:27:58

Perl閉包
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久一级 | 中文字幕一区二区三区四区五区 | 一级黄片一级毛片 | av香蕉 | 久久99精品久久久久久国产越南 | 国产91av视频| 九九久久在线看 | 欧美日韩国产传媒 | 国产三区在线观看视频 | 宅男伊人| 久草电影网 | 免费黄色av网站 | 欧美一页| 欧美激情久久久 | 精品一区二区三区免费视频 | 亚洲精品粉嫩美女一区 | 欧美在线观看免费观看视频 | 国产精品美女久久久久久久久久久 | 精品欧美一区二区三区久久久 | 亚洲欧美在线观看 | 香蕉超碰 | 亚洲精品久久久久久久久久久久久 | 欧洲性生活视频 | 日韩人体视频 | 精品欧美| 精品国产一区二区三区性色 | av一区二区三区四区 | 成人免费毛片片v | 日韩欧美三区 | 国产成人免费视频网站高清观看视频 | 久久久久亚洲 | 国产99久久精品一区二区永久免费 | 99av成人精品国语自产拍 | 久久成人精品视频 | 亚洲国产精品久久人人爱 | 久久久噜噜噜久久中文字幕色伊伊 | 欧美精品一区二区三区蜜桃视频 | 国产一区二区三区视频在线观看 | 国产精品一区久久久 | 成人精品国产一区二区4080 | 亚洲精品视频一区 |