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

React异步函数中组件状态读写异常的修复方法

问题分析与解决方案

你的核心问题出在React状态的闭包特性和状态管理分散上:

  1. 异步函数sendRequests捕获的是hasErrors的初始值(false),即便调用setHasErrors(true)更新状态,函数内的hasErrors不会同步更新,导致最后判断!hasErrors时依然会错误触发模态框。
  2. useEffect依赖数组为空,未包含props.requests等关键依赖,会导致请求列表更新时无法重新执行逻辑。
  3. 状态分散在useState和useReducer中,不利于统一跟踪请求状态。

下面给出两种可行的修复方案:


方案一:将所有状态整合到Reducer(推荐)

把请求报告和错误状态统一交给Reducer管理,避免闭包导致的状态不同步问题。

修改后的代码

function requestReportReducer(state, action) {
    switch (action.type) {
        case 'append':
            return {
                ...state,
                reports: [...state.reports, action.payload],
                // 只要有一个请求失败,就把hasErrors设为true并保持
                hasErrors: state.hasErrors || !action.payload.isSuccessful
            };
        case 'reset':
            return { reports: [], hasErrors: false };
        default:
            return state;
    }
}

export default function RequestList(props) {
    const [state, dispatch] = useReducer(requestReportReducer, { reports: [], hasErrors: false });
    const { reports, hasErrors } = state;

    useEffect(() => {
        // 每次props更新时重置状态,避免残留旧数据
        dispatch({ type: 'reset' });

        async function sendRequests() {
            for (const requestData of Object.values(props.requests)) {
                // 注意:这里需要把requestData传给sendRequest,否则无法发送对应请求
                const response = await sendRequest(requestData);
                const isSuccessful = response.statusCode < 400;

                dispatch({
                    type: 'append',
                    payload: {
                        isSuccessful,
                        responseCode: response.statusCode,
                        responseBody: response.body,
                    }
                });
            }

            // 直接从Reducer的最新状态判断是否自动打开模态框
            if (!state.hasErrors) {
                showAllRequestsAreSentModal();
            }
        }

        // 避免空请求列表时执行无效逻辑
        if (Object.values(props.requests).length > 0) {
            sendRequests();
        }
    }, [props.requests]); // 添加props依赖,确保请求列表更新时重新执行

    return (
        <div>
            <dl>
                {reports.map((requestReport, index) => (
                    <p key={index}>
                        {requestReport.isSuccessful ? "请求成功" : "请求失败"} 
                        - 状态码: {requestReport.responseCode}
                    </p>
                ))}
            </dl>
            {hasErrors && (
                <button onClick={showAllRequestsAreSentModal}>Finish execution</button>
            )}
        </div>
    );
}

关键修改说明

  1. 统一状态管理:将reports和hasErrors合并到Reducer中,每次更新状态时都能获取到最新值,彻底解决闭包问题。
  2. 自动更新错误状态:在append动作中,只要有一个请求失败,hasErrors就会被设为true并保持。
  3. 修正依赖数组:加入props.requests,确保请求列表变化时重新执行请求逻辑,同时重置状态避免数据残留。
  4. 修复请求调用:给sendRequest传入循环的requestData,保证发送对应请求。

方案二:用本地变量跟踪错误状态

如果不想修改Reducer结构,可以在异步函数内用本地变量跟踪错误,避免依赖React状态的闭包值。

修改后的代码

function requestReportReducer(state, action) {
    switch (action.type) {
        case 'append':
            return [...state, action.payload];
        default:
            return state;
    }
}

export default function RequestList(props) {
    const [report, reduceReport] = useReducer(requestReportReducer, []);
    const [hasErrors, setHasErrors] = useState(false);

    useEffect(() => {
        async function sendRequests() {
            // 用本地变量跟踪错误,不受闭包影响
            let hasAnyError = false;

            for (const requestData of Object.values(props.requests)) {
                const response = await sendRequest(requestData);
                const isSuccessful = response.statusCode < 400;

                reduceReport({
                    type: 'append',
                    payload: {
                        isSuccessful,
                        responseCode: response.statusCode,
                        responseBody: response.body,
                    }
                });

                if (!isSuccessful) {
                    hasAnyError = true;
                    setHasErrors(true);
                }
            }

            // 用本地变量判断是否自动打开模态框
            if (!hasAnyError) {
                showAllRequestsAreSentModal();
            }
        }

        sendRequests();
    }, [props.requests]);

    return (
        <div>
            <dl>
                {report.map((requestReport, index) => (
                    <p key={index}>
                        {requestReport.isSuccessful ? "请求成功" : "请求失败"} 
                        - 状态码: {requestReport.responseCode}
                    </p>
                ))}
            </dl>
            {hasErrors && (
                <button onClick={showAllRequestsAreSentModal}>Finish execution</button>
            )}
        </div>
    );
}

关键修改说明

  • 用本地变量hasAnyError在循环中跟踪错误状态,完全避开React状态的闭包限制。
  • 同时更新hasErrors的React状态,保证JSX能正确渲染错误按钮。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:11:23