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

React-query useQuery重复调用问题及按需API调用需求

问题解决:React-Query 初始API请求重复调用的处理

问题原因

fetchPreviousOverallStatus被多次调用,核心是React-Query的默认配置触发了自动重复获取:

  • refetchOnWindowFocus: true:窗口聚焦时自动重新请求
  • refetchOnReconnect: true:网络恢复后自动重新请求
  • staleTime: 0:数据加载完成后立即标记为"过时",触发自动刷新

这些默认行为会导致初始请求完成后,额外触发重复请求。

解决方案

1. 配置初始查询,禁止自动重复获取

修改useQuery的配置,关闭自动重获取并设置永久新鲜的缓存规则:

const { isLoading, data, isError, error, isFetching, refetch } = useQuery('overallPreviousStatus', fetchPreviousOverallStatus, {
    onSuccess,
    onError,
    enabled: true,
    refetchOnWindowFocus: false, // 关闭窗口聚焦时的自动重获取
    refetchOnReconnect: false, // 关闭网络恢复后的自动重获取
    staleTime: Infinity, // 标记数据永久新鲜,不会触发自动重获取
})

2. 优化模态框查询的触发逻辑

无需通过useEffect手动调用refetchQueries,直接用enabled条件控制请求触发,同时在onSuccess中更新状态:

// 移除原有的fetchPreviousData函数和对应useEffect
// 替换模态框的useQuery配置
const { isLoading:isLoading1, data:data1, isError:isError1, error:error1, isFetching:isFetching1 } = useQuery('previousOcsTestResults', fetchPreviousOcsTestResults, {
    onSuccess: (res) => {
        setApiData(res.data); // 请求成功后直接更新状态
        console.log("Perform side effect after success", res.data);
    },
    onError,
    enabled: isOpen && dataToFetch === "previous", // 仅当模态框打开且需获取previous数据时触发请求
    refetchOnWindowFocus: false,
})

3. 避免组件不必要重渲染(可选)

如果父组件频繁重渲染,或environmentMapping每次渲染生成新对象,给useSelector添加深比较器,避免无意义的重渲染:

const environmentMapping = useSelector(
    (state) => state.env.environmentMapping,
    (prev, next) => JSON.stringify(prev) === JSON.stringify(next) // 深比较避免浅比较导致的重渲染
);

完整修改后的代码片段

import React, {useState, useMemo} from "react"
import Card from "../Common/Card"
import {useSelector} from "react-redux"
import Overlay from "../Common/Overlay"
import axios from "axios"
import {apiSubDirectory} from "../../Constants"
import {useQuery} from "react-query"

export default function OverallOrderCommunicationServiceStatus() {

    const onSuccess = (data) => {
        console.log("Perform side effect after success", data.data)
    }
    const onError = (error) => {
        console.log('Perform side effect after error', error)
    }

    const [isOpen, setIsOpen] = useState(false);
    const [dataToFetch, setDataToFetch] = useState("");
    const [apiData, setApiData] = useState([]);

    const environmentMapping = useSelector(
        (state) => state.env.environmentMapping,
        (prev, next) => JSON.stringify(prev) === JSON.stringify(next)
    );

    const urlOverallPreviousStatus = `https://jsonplaceholder.typicode.com/posts`

    const fetchPreviousOverallStatus = () => { return axios.get(urlOverallPreviousStatus) }

    const { isLoading, data, isError, error, isFetching } = useQuery('overallPreviousStatus', fetchPreviousOverallStatus, {
        onSuccess,
        onError,
        enabled: true,
        refetchOnWindowFocus: false,
        refetchOnReconnect: false,
        staleTime: Infinity
    })


    const fetchPreviousOcsTestResults = () => {
        return axios.get(`https://jsonplaceholder.typicode.com/posts?a=${environmentMapping[0].previous.environment}`)
    }

    const { isLoading:isLoading1, data:data1, isError:isError1, error:error1, isFetching:isFetching1 } = useQuery('previousOcsTestResults', fetchPreviousOcsTestResults, {
        onSuccess: (res) => {
            setApiData(res.data);
            console.log("Perform side effect after success", res.data);
        },
        onError,
        enabled: isOpen && dataToFetch === "previous",
        refetchOnWindowFocus: false
    })

    const columns = useMemo(() => [
        {
            Header: "Status",
            accessor: "title",
        },
    ], [])

    const handlePopupOpen = (dataToFetch) => {
        setIsOpen(true);
        setDataToFetch(dataToFetch);
    };

    const handlePopupClose = () => {
        setIsOpen(false);
    };

    return (
        <React.Fragment>
            <div className="row">
                <div className="col-md-12 ml-sm-auto col-lg-12 mt-5">
                    <div className="row mt-5">
                        <div className="card-group">
                            <Card
                                environmentName={environmentMapping[0].previous.environment}
                                releaseno="Environment"
                                onClick={() => handlePopupOpen("previous")}
                            />

                            {environmentMapping[0].previous.isEnvironmentReady ? (
                                isOpen && (
                                    <Overlay
                                        environment={environmentMapping[0].previous.environment}
                                        onClose={handlePopupClose}
                                        show={isOpen}
                                        columns={columns}
                                        apiData={apiData}
                                    />
                                )
                            ) : (
                                <React.Fragment />
                            )}

                        </div>
                    </div>
                </div>
            </div>
        </React.Fragment>
    );
}

为什么之前的尝试无效

你尝试在组件卸载时禁用查询的方法,仅对组件卸载后的行为生效,但初始请求的重复调用发生在组件挂载后,是React-Query的自动重获取机制导致的,因此无法解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:35:35