React hook useState 的异步引起的bug
线上代码参考:
https://codepen.io/hellomrbig...
使用 react hook 做了一个类似 tooltip 的组件。将鼠标放在灰色块上会显示蓝色块。但是现在有一个问题,灰色块和蓝色块中间有一个间隙,鼠标移动到这个间隙的时候蓝色块会消失。为此我在鼠标移除的方法中添加了一个延时函数,并且加了一个标识符 visible2
const handleMouseLeave = (type) => {
console.log('leave', type)
if (type === 'main') {
setTimeout(() => {
console.log('visible', visible, 'visible2', visible2)
if (!visible2 && visible) { // visible2 用来标识鼠标是否移动到蓝色块内
setVisible(false)
}
}, 1000)
} else if (type === 'content') {
// visible2 = false
setVisible2(false)
setVisible(false)
}
}
事件的触发顺序应该是 离开灰色块 -> 进入蓝色块 -> setVisible2(true) 这时才触发灰色块 handleMouseLeave
中的这段代码,此时 visible2 已经是 true 了,所以不会触发 setVisible(false)
,导致蓝色块消失。
console.log('visible', visible, 'visible2', visible2)
if (!visible2 && visible) { // visible2 用来标识鼠标是否移动到蓝色块内
setVisible(false)
}
但是在功能演示时发现鼠标移动到蓝色块中后过了延时时间蓝色块还是消失了。。。并且console.log('visible', visible, 'visible2', visible2)
打印出来的结果是visible true visible2 false
说明触发了蓝色块中的 handleMouseEnter
方法中的 setVisible2(true)
,visible2
还是 false
。我就有点懵,虽然 setVisible2
是异步函数,但是我的延时函数应该会在它结束后执行。所以这里是不是我对 useState
的理解有点问题。
解决方法是将标识符 visible2
用 let
声明(参考我注释的代码)。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
(1)原因:
setState
会导致Function组件的重新执行,但是setTimeout
捕获的变量是前一轮更新的,所以visible2
会一直为false,所以导致代码的行为不符合预期详细的原因我在这个回答中有解释(2)解决方法:
如果就是要使用
useState
保存状态visible2
,而且要使代码行为符合预期那么只需要让setTimeout
第一个函数中捕获的变量是新一轮更新的即可,所以我们可以对代码做以下更改在线体验地址这样的话setTimeout中执行的函数捕获的变量就是最新的了,因为组件每次重新执行是我们都把
latestFunc.current
指向到了新创建的函数,更多的相关问题可以看我的这个回答(3) 为什么把
visible2
放在let声明的普通变量中就能正常呢如果前面的东西你都看了的话,那么你就会知道
setVisible2
会导致函数组件的重新执行,进而导致setTimeout
中函数捕获的变量出现问题,而直接给普通变量赋值不会导致重新执行函数组件,就不会出现这种问题,所以等到setTimeout执行时,他其中捕获的变量并不会出现什么问题把visible2换成ref把(虽然感觉这个需求在css上动动手脚好像也可以
鼠标进入蓝色区域 需要把定时器清掉
想复杂了吧,延迟隐藏,进入就清掉延迟器
这是一个闭包问题!