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

react-query中useQuery的refetch如何单独重请求未出错API?

问题描述

我正在ReactJS中使用react-query构建网站,现有如下使用useQuery()获取数据的代码:

const { error, data: movie, status, refetch } = useQuery({
    queryKey: 'key1',
    queryFn: async () => {
        return await Promise.all([
            axios.get(`API-1.com/get-some-data`), /*getting some data from api-1 */
            axios.get(`API-2.com/get-some-data`), /*getting some data from api-2 */
        ]).then(([api1, ap2]) => {
            return { data1: api1, data2: api2 }
        })
    }
})

目前遇到的问题是,其中一个API偶尔会返回404响应,我需要仅重新请求出错的那个API。我尝试在onError中使用refetch方法,但该方法无法传入参数,代码如下:

onError: (error) => {
    refetch() /* refetch can't be used with parameters */
},

请问该如何解决这个问题?

解决方案

方案一:拆分独立useQuery实例

将两个API请求拆分为单独的useQuery,每个请求拥有独立的状态控制和重试逻辑,是最直观的解决方式:

import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

// 单独请求API-1
const { 
    data: data1, 
    error: error1, 
    refetch: refetchApi1 
} = useQuery({
    queryKey: ['movie-api-1'],
    queryFn: () => axios.get(`API-1.com/get-some-data`),
    retry: false // 关闭默认重试,按需自定义
});

// 单独请求API-2
const { 
    data: data2, 
    error: error2, 
    refetch: refetchApi2 
} = useQuery({
    queryKey: ['movie-api-2'],
    queryFn: () => axios.get(`API-2.com/get-some-data`),
    retry: false
});

// 合并两个接口的数据,保持原有逻辑
const movie = { data1, data2 };

针对单个请求添加重试逻辑

如果需要在特定API返回404时自动重试,可在对应useQuery中配置onError或retry规则:

// API-1遇到404时延迟1秒重试
const { refetch: refetchApi1 } = useQuery({
    queryKey: ['movie-api-1'],
    queryFn: () => axios.get(`API-1.com/get-some-data`),
    retry: false,
    onError: (error) => {
        if (error.response?.status === 404) {
            setTimeout(() => refetchApi1(), 1000);
        }
    }
});

方案二:在原queryFn中单独处理错误(不拆分请求)

如果不想拆分请求,可使用Promise.allSettled替代Promise.all,保留成功请求的结果,仅重试失败的接口:

import { useQuery, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

const queryClient = useQueryClient();

// 定义单个API请求函数,标记错误来源
const fetchApi1 = async () => {
    try {
        return await axios.get(`API-1.com/get-some-data`);
    } catch (err) {
        err.source = 'api1';
        throw err;
    }
};

const fetchApi2 = async () => {
    try {
        return await axios.get(`API-2.com/get-some-data`);
    } catch (err) {
        err.source = 'api2';
        throw err;
    }
};

const { error, data: movie } = useQuery({
    queryKey: ['movie-data'],
    queryFn: async () => {
        // 用allSettled确保所有请求完成,不管成功失败
        const [api1Res, api2Res] = await Promise.allSettled([fetchApi1(), fetchApi2()]);
        const result = {};
        
        // 收集成功结果
        if (api1Res.status === 'fulfilled') result.data1 = api1Res.value;
        if (api2Res.status === 'fulfilled') result.data2 = api2Res.value;
        
        // 收集错误并抛出,供onError处理
        const errors = [];
        if (api1Res.status === 'rejected') errors.push(api1Res.reason);
        if (api2Res.status === 'rejected') errors.push(api2Res.reason);
        
        if (errors.length > 0) throw new Error('部分请求失败', { cause: errors });
        return result;
    },
    onError: async (err) => {
        // 针对每个错误来源单独重试并更新缓存
        for (const errItem of err.cause) {
            if (errItem.source === 'api1') {
                const newData1 = await fetchApi1();
                queryClient.setQueryData(['movie-data'], (old) => ({ ...old, data1: newData1 }));
            } else if (errItem.source === 'api2') {
                const newData2 = await fetchApi2();
                queryClient.setQueryData(['movie-data'], (old) => ({ ...old, data2: newData2 }));
            }
        }
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:50:26