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

React.js:异步函数无法获取最新state值的问题求助

问题原因

你的问题源于JavaScript闭包的特性:当processing为true时,useEffect执行并调用了toggleProcessing异步函数,该函数内部的processing变量是闭包捕获的当时的true值。后续点击按钮将processing改为false时,虽然useEffect会因依赖更新再次触发,但此时processing为false,不会进入if (processing)分支执行新的toggleProcessing,而之前运行的异步函数仍持有旧的true值,导致while循环持续执行。

解决方案

以下两种方案都能解决这个问题:

方案1:用useRef跟踪最新状态

useRef的current属性是可变的,不会被闭包固定值,能让异步函数随时获取最新的状态值:

import React, { useState, useEffect, useRef } from 'react'

const App = () => {
    const [processing, setProcessing] = useState(false)
    const processingRef = useRef(processing)

    // 同步ref与state的最新值
    useEffect(() => {
        processingRef.current = processing
    }, [processing])

    const sleep = (ms) => {
        return new Promise(resolve => setTimeout(resolve, ms))
    }

    useEffect(() => {
        let isCancelled = false;
        const toggleProcessing = async () => {
            // 每次循环读取ref的最新值
            while (processingRef.current && !isCancelled) {
                console.log('Processing...')
                await sleep(2000);
            }
            if (!isCancelled) {
                console.log('Stopping Processing')
            }
        }
        if (processing) {
            toggleProcessing()
        }
        // 清理函数:标记任务取消,避免重复日志
        return () => {
            isCancelled = true;
        }
    }, [processing])

    return (
        <>
            <button onClick={() => setProcessing(current => !current)}>{processing ? 'Stop Processing' : 'Begin Processing'}</button>
        </>
    )
}

export default App;

方案2:通过清理函数终止异步任务

利用useEffect的清理函数,在状态变化时直接终止循环:

import React, { useState, useEffect} from 'react'

const App = () => {
    const [processing, setProcessing] = useState(false)

    const sleep = (ms) => {
        return new Promise(resolve => setTimeout(resolve, ms))
    }

    useEffect(() => {
        let shouldContinue = true;
        const toggleProcessing = async () => {
            while (shouldContinue) {
                console.log('Processing...')
                await sleep(2000);
            }
            console.log('Stopping Processing')
        }
        if (processing) {
            toggleProcessing()
        }
        // 当processing变化时,执行清理函数终止循环
        return () => {
            shouldContinue = false;
        }
    }, [processing])

    return (
        <>
            <button onClick={() => setProcessing(current => !current)}>{processing ? 'Stop Processing' : 'Begin Processing'}</button>
        </>
    )
}

export default App;
方案说明
  • 方案1中,processingRef始终同步最新的processing状态,异步函数每次循环都会读取最新值,状态变为false时循环自动退出。清理函数的isCancelled用于避免任务取消后打印多余日志。
  • 方案2中,当processing从true切换为false时,前一次useEffect的清理函数会触发,将shouldContinue设为false,循环条件不满足后退出并打印停止日志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:30:59