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

如何在使用async时递归更新React useState状态?

问题原因

你遇到的问题核心是闭包捕获了旧的state值:组件第一次渲染时,btnClick和increase函数会捕获当时的value(初始值-1)。当递归调用btnClick时,虽然setValue已经更新了state触发重渲染,但递归调用的是第一次渲染时创建的btnClick函数,里面的value还是旧的-1,所以每次increase计算的都是-1+1=0,重复设置同一个值,看起来就只增长了一次。

解决方案

方案1:使用函数式更新state

React的setValue支持传入函数,函数的参数是当前最新的state值,这样就能绕过闭包的问题,每次都获取到最新的value:

export const AsyncTest = ()=>{
    const [value, setValue] = useState(-1);
    const [limit] = useState(5);

    const btnClick = async ()=>{
        const test = await increase();
        if (!test) {
            btnClick();
        } else {
            // 完成后的逻辑
        }
    }

    const increase = ()=>{
        return new Promise (resolve =>{
            window.setTimeout(()=>{
                // 使用函数式更新,获取最新的value
                setValue(prevValue => {
                    const valueNew = prevValue + 1;
                    if (valueNew <= limit) {
                        resolve(false);
                        return valueNew;
                    } else {
                        resolve(true);
                        return prevValue;
                    }
                });
            }, 1000);
        })
    }
    return (
        <button id="asyncTestBtn" onClick={btnClick}>{value}</button>
    )
}

方案2:用useRef保存最新值

如果不想修改setValue的方式,可以用useRef存储value的最新值,因为ref的.current属性是可变的,不会被闭包捕获旧值:

export const AsyncTest = ()=>{
    const [value, setValue] = useState(-1);
    const valueRef = useRef(value);
    const [limit] = useState(5);

    // 每次state更新时同步ref的值
    useEffect(() => {
        valueRef.current = value;
    }, [value]);

    const btnClick = async ()=>{
        const test = await increase();
        if (!test) {
            btnClick();
        } else {
            // 完成后的逻辑
        }
    }

    const increase = ()=>{
        return new Promise (resolve =>{
            window.setTimeout(()=>{
                let valueNew = valueRef.current + 1;
                if (valueNew <= limit){
                    setValue(valueNew);
                    resolve(false);
                } else {
                    resolve(true);
                }
            }, 1000);
        })
    }
    return (
        <button id="asyncTestBtn" onClick={btnClick}>{value}</button>
    )
}

手动点击正常的原因

手动点击时,每次点击都会触发最新渲染生成的btnClick函数(state更新后组件重渲染,会重新创建btnClick),所以里面的value是最新的,自然能正常增长到上限。

内容的提问来源于stack exchange,提问作者Adam Shaw

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:48:14