You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 00:18:26