0.背景
什么是闭包?
为什么要存在闭包?
1.不同React Hooks中的闭包陷阱的表现
1.1 useState中的陷阱表现
具体问题表现场景是:在异步回调中获取不到最新的state状态值
例1:
js
import React, { useState, useEffect } from 'react';
const App = () => {
const [arr, setArr] = useState([0]);
useEffect(() => {
console.log(arr);
}, [arr]);
const handleClick = () => {
Promise.resolve().then(() => {
setArr([...arr, 1]); // 此时赋值前 arr 为:[0],异步回调中更新了state状态
})
.then(() => {
setArr([...arr, 2]); // 此时赋值前 arr 为旧状态仍然为:[0],在第二个异步回调中拿到的state值不是前面更新过的
});
}
// 我们希望打印的是[0], [0,1], [0,1,2]
// 实际上打印的而是[0], [0,1], [0,1]
return (
<>
<button onClick={handleClick}>change</button>
</>
);
}
export default App;
经典例2:
js
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>
);
}
我们每次点击之后,开启一个异步的定时器回调,然后在定时器中执行更新setState函数。假设定时器的执行间隔是1s。那么我在1s内连续点击5次,对应的函数回调会设置5个定时器回调函数,每隔1s就会执行setState操作来更新state状态。每次回调应该拿到最新的state状态值。
(1) 对应原因
(2) 解决方案
参考:www.cnblogs.com/hymenhan/p/...
1.2 useEffect中的陷阱表现
js
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>
);
}
在这个例子中,我们在点击事件回调中使用setState来更新state状态,然后 我们使用了 useState 和 useEffect Hooks。在 useEffect 回调函数内部,我们使用了一个 setInterval 函数来输出 count 状态变量。然而,由于 useEffect 只会在组件首次渲染时执行一次,因此闭包中的 count 变量始终是首次渲染时的变量,而不是最新的值。
(1) 对应原因
(2) 解决方案
useEffect中闭包表现对应参考文献: segmentfault.com/a/119000004...
2.总结
对于这个问题能给我带来什么?我们需要注意在异步中更新state状态后,后续的是否可以拿到最新的state状态值。
问题1:组件每次更新UI的时候,拿到的state是从哪里获取的,是在hook-useState中的memoizedState中拿的吗?
问题2:hook如何获取数据
问题3:hook如何更新数据?
当我们使用useState的时候,返回的第二个setState函数可以认为是dispatch函数,我们使用这个函数来更新state状态,就可以实现视图UI的更新。 那么我们使用setState的时候发生了什么呢?首先,先考虑state的数据的更新,在考虑视图的更新。 数据方面,调用一次setState会对应创建一个update对象
hooks节点中的memoizedState和baseState,在存储的数据一定是相同的吗?如果不同,为什么要这么设置?
答:basestate是当前组件在更新过程中,基于已更新的state的临时存储值。而memoizedstate是当前组件所有的setState更新完毕之后最终存储的state最终值。UI视图上渲染的就是拿的这个memoizedState值。之所以要额外设置baseState是因为考虑在更新过程中,存在优先级临时插队的"中断"情况,因此需要中断的时候及时存储对应当前计算出来的"临时值"。