React使用delay延迟时useState状态取值不一致问题
问题描述
组件嵌套了子组件,需求为仅当用户鼠标悬停时长达到短延迟阈值后,才展示内部子组件。
实现逻辑为编写onMouseEnter与onMouseExit事件处理函数,设置状态变量标识当前是否处于悬停状态,等待延迟结束后,若仍处于悬停状态则设置第二个状态变量控制对应元素展示。
实际运行时出现异常:无论如何操作,延迟回调内的hovered取值始终为false。组件顶层作用域的console.log可以正确打印state值,只有异步回调内取值异常,问题代码如下:
const [hovered, setHovered] = useState(false); const [show, setShow] = useState(false); console.log('hovered', hovered); // 此处可正确获取并打印state值 const handleEnter = () => { setHovered(true); sleep(2000).then(() => { console.log('checking', hovered); // 此处始终打印false if (hovered) { setShow(true); } }); } const handleExit = () => { setHovered(false); setShow(false); }
问题原因
该问题由React闭包陷阱导致:
事件处理函数handleEnter在组件首次渲染时创建,闭包内捕获的hovered是初始值false。调用setHovered(true)会触发组件重渲染,新渲染周期中hovered更新为true,但sleep的异步回调仍绑定首次渲染时handleEnter闭包内的旧值,因此回调中读取到的hovered始终为false。
解决方案
将原生sleep延迟替换为use-debounce提供的包装回调,避免回调重复触发,同时保留原有延迟效果;配合useRef存储最新的悬停状态,绕开闭包对旧状态的捕获问题,实现代码如下:
const ref = useRef(false); const [hovered, setHovered] = useState(false); const [show, setShow] = useState(false); const handleHover = useDebouncedCallback(() => { if (ref.current) setShow(true); else setShow(false); }, 1000); useEffect(() => { ref.current = hovered; }, [hovered]); useEffect(() => { handleHover() }, [hovered]);
内容的提问来源于stack exchange,提问作者cclloyd
相关产品推荐
相关产品推荐

