使用Axios+TanstackQuery时请求成功但AxiosResponse.data为undefined
问题分析与解决方案
核心问题出在Axios泛型参数的误用,导致你拿到的query.data并不是完整的Axios响应对象,而是直接对应响应的data字段类型,所以再访问.data自然会得到undefined。
为什么非泛型版本正常?
非泛型版本中,你可能没有指定Axios的泛型参数,或者显式指定了AxiosResponse<具体数据类型>,此时query.data是完整的Axios响应对象,包含data属性,所以能正常取值。而泛型版本中,你把<T>传给了axios.get,但Axios的get方法泛型参数默认指代的是响应体data的类型,而非整个响应对象的类型。
解决方案1:保留完整Axios响应对象
如果你需要获取完整的Axios响应(比如headers、status等),需要显式指定泛型为AxiosResponse<T>:
import { AxiosResponse } from 'axios'; const fetchDataService = <T>(key: string, url: string, enabled: boolean): Service<T> => { const [result, setResult] = useState<Service<T>>({ status: ServiceStatus.LOADING }); const { apiBaseEndpoint } = useContext(ApiEndpointsContext); const query = useQuery({ queryKey: [key], retry: false, enabled, staleTime: 1000 * 10, // 指定泛型为AxiosResponse<T>,此时query.data是完整的响应对象 queryFn: async ({ signal }) => await axios.get<AxiosResponse<T>>(`${apiBaseEndpoint}${url}`, { signal }) }); useEffect(() => { if (query.isLoading || query.isFetching) return; if (query.isError) { setResult({ status: ServiceStatus.ERROR, error: query.error as Error }); return; } const axiosResponse = query.data; if (!axiosResponse) { setResult({ status: ServiceStatus.ERROR, error: new Error('Unknown error occurred when fetching data') }); return; } // 此时可以正常访问axiosResponse.data setResult({ status: ServiceStatus.SUCCESS, payload: axiosResponse.data }); }, [query.isFetching, query.isLoading, query.isError, query.data, query.error]); return result; };
解决方案2:直接返回响应数据(推荐)
通常我们只需要响应体的data字段,此时可以让Axios直接返回data,简化后续逻辑:
const fetchDataService = <T>(key: string, url: string, enabled: boolean): Service<T> => { const [result, setResult] = useState<Service<T>>({ status: ServiceStatus.LOADING }); const { apiBaseEndpoint } = useContext(ApiEndpointsContext); const query = useQuery({ queryKey: [key], retry: false, enabled, staleTime: 1000 * 10, // 泛型T指代响应data的类型,直接返回response.data queryFn: async ({ signal }) => { const response = await axios.get<T>(`${apiBaseEndpoint}${url}`, { signal }); return response.data; } }); useEffect(() => { if (query.isLoading || query.isFetching) return; if (query.isError) { setResult({ status: ServiceStatus.ERROR, error: query.error as Error }); return; } if (!query.data) { setResult({ status: ServiceStatus.ERROR, error: new Error('Unknown error occurred when fetching data') }); return; } // 直接用query.data作为payload,无需再访问.data属性 setResult({ status: ServiceStatus.SUCCESS, payload: query.data }); }, [query.isFetching, query.isLoading, query.isError, query.data, query.error]); return result; };
额外优化:去掉冗余的状态同步
React Query本身已经维护了请求状态,你完全不需要用useState和useEffect同步状态,可以直接根据query的状态返回结果,简化代码:
const fetchDataService = <T>(key: string, url: string, enabled: boolean): Service<T> => { const { apiBaseEndpoint } = useContext(ApiEndpointsContext); const query = useQuery({ queryKey: [key], retry: false, enabled, staleTime: 1000 * 10, queryFn: async ({ signal }) => { const response = await axios.get<T>(`${apiBaseEndpoint}${url}`, { signal }); return response.data; } }); if (query.isLoading || query.isFetching) { return { status: ServiceStatus.LOADING }; } if (query.isError) { return { status: ServiceStatus.ERROR, error: query.error as Error }; } if (!query.data) { return { status: ServiceStatus.ERROR, error: new Error('Unknown error occurred when fetching data') }; } return { status: ServiceStatus.SUCCESS, payload: query.data }; };
这样既避免了状态不同步的风险,代码也更简洁。
内容的提问来源于stack exchange,提问作者Zahand
相关产品推荐
相关产品推荐

