react hooks的闭包陷阱(setInterval不生效)问题
程序员文章站
2022-04-06 15:12:37
...
在使用react hooks时, 会遇到这样的问题
const [count, setCount] = useState(1)
useEffect(() => {
setInterval(() => {
console.log(count)
}, 1000)
//闭包陷阱
}, [])
const handleClick = () => {
setCount(count+1)
}
return (
<div onClick={handleClick}>
click to add, count: {count}
</div>
)
我们期待在点击之后, 打印出count也能更新, 但是事实是, 每次打印出的count都没变.
这就涉及到闭包陷阱的问题了
什么是闭包陷阱
简单来说, 就是react hooks在渲染的时候维护了一个链表, 来记录useState和useEffect的位置和值, (这也是state不能使用if else的原因, 因为可能会导致链表中state useEffect的顺序错乱, 从而不能获取到正确的数值)
在每次state更新时, 链表从头开始重新渲染, 但是由于上面示例中useEffect
没有依赖任何state
, 所以只有在第一次渲染的时候才会触发, setCount
渲染更新时, useEffect
里面的回调函数并没有触发 因此里面的setInterval
里面的count还是初始化时的值,
并没有获取到最新的. 这就是闭包陷阱
怎么解决
使用useRef可以解决闭包陷阱的问题, 为什么?
因为useRef 每次拿到的都是这个对象本身, 是同一个内存空间的数据, 所以可以获取到最新的值
同理, 我们如果这样浅拷贝, 也是可以获取到最新的值的
useEffect(() => {
setInterval(() => {
console.log(count)
}, 1000)
//闭包陷阱
}, [])
const handleClick = () => {
setCount((prevState) => {
//浅拷贝
return Object.assign(prevState, {
name: 'ssdsd',
})
})
setDate('yesterday')
}
return (
<div onClick={handleClick}>
click to add, count: {count.a}, name:{count.name}
</div>
)
在点击后setInterval
中的值也会更新, 因为本质上就是同一个内存空间的一个对象
或者这样, 也可以证明上述结论
const handleClick = () => {
setCount((prevState) => {
//浅拷贝
let aaa = prevState
aaa.name = 'sfss'
return aaa
})
setDate('yesterday')
}