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
相关产品推荐
相关产品推荐

