偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

其實(shí)那個(gè)案例的閉包陷阱的解決方式不夠完善,這篇文章我們?cè)偻晟埔幌隆?/p>

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

hooks 的閉包陷阱是指 useEffect 等 hook 中用到了某個(gè) state,但是沒有把它加到 deps 數(shù)組里,導(dǎo)致 state 變了,但是執(zhí)行的函數(shù)依然引用著之前的 state。

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

我們舉了這樣一個(gè)例子:

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 設(shè)置到 deps 里,并添加清理函數(shù):

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;

這樣就能解決閉包陷阱:

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

為什么呢?

因?yàn)楝F(xiàn)在每次 count 變了就會(huì)重置定時(shí)器,那之前的計(jì)時(shí)就重新計(jì)算,這樣就會(huì)導(dǎo)致計(jì)時(shí)不準(zhǔn)。

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

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

useRef。

閉包陷阱產(chǎn)生的原因就是 useEffect 的函數(shù)里引用了某個(gè) state,形成了閉包,那不直接引用不就行了?

useRef 是在 memorizedState 鏈表中放一個(gè)對(duì)象,current 保存某個(gè)值。

它的源碼是這樣的:

初始化的時(shí)候創(chuàng)建了一個(gè)對(duì)象放在 memorizedState 上,后面始終返回這個(gè)對(duì)象。

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

也就是這樣:

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

useEffect 里執(zhí)行定時(shí)器,deps 設(shè)置為了 [],所以只會(huì)執(zhí)行一次,回調(diào)函數(shù)用的是 ref.current,沒有直接依賴某個(gè) state,所以不會(huì)有閉包陷阱。

用 useRef 創(chuàng)建個(gè) ref 對(duì)象,初始值為打印 count 的回調(diào)函數(shù),每次 render 都修改下其中的函數(shù)為新創(chuàng)建的函數(shù),這個(gè)函數(shù)里引用的 count 就是最新的。

這里用了 useLayoutEffect 而不是 useEffect 是因?yàn)?useLayoutEffect 是在 render 前同步執(zhí)行的,useEffect 是在 render 后異步執(zhí)行的,所以用 useLayoutEffect 能保證在 useEffect 之前被調(diào)用。

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

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

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

現(xiàn)在組件的代碼是這樣的:

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;

測(cè)試下:

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

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

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

然后組件代碼就可以簡(jiǎn)化了:

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

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

總結(jié)

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

這種方式用在定時(shí)器上是不合適的,因?yàn)槎〞r(shí)器一旦被重置和重新計(jì)時(shí),那計(jì)時(shí)就不準(zhǔn)確了。

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

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

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

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

  • 設(shè)置依賴的 state 到 deps 數(shù)組中并添加清理函數(shù)。
  • 不直接引用 state,把 state 放到 useRef 創(chuàng)建的 ref 對(duì)象中再引用。

處理定時(shí)器的時(shí)候,為保證計(jì)時(shí)的準(zhǔn)確,最好使用 useRef 的方式,其余情況兩種都可以。

責(zé)任編輯:姜華 來源: 神光的編程秘籍
相關(guān)推薦

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數(shù)組函數(shù)

2023-11-06 08:00:00

ReactJavaScript開發(fā)

2017-05-22 16:08:30

前端開發(fā)javascript閉包

2020-07-29 10:10:37

HTTP緩存前端

2020-10-28 09:12:48

React架構(gòu)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閉包
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)