React组件中Axios Get请求返回空数组问题排查求助
问题描述
PlayingCard组件通过自定义Hook useCards 从后端加载数据,但组件中的data变量始终为空数组。浏览器直接访问接口URL能正常返回JSON,Network面板的payload预览也能看到有效数据,怀疑问题出在前端。
相关代码
PlayingCard组件代码
import { Card, CardBody, CardFooter, Flex, Image, Spacer, } from "@chakra-ui/react"; import CardIcon from "./CardIcon"; import DescriptionText from "./DescriptionText"; import placeholder from "../images/placeholder.jpg"; import useCards, { GauntletCard } from "../hooks/useCards"; const PlayingCard = () => { const { data } = useCards(); //console.log(data[0]); return ( <Card height="350px" width="250px" backgroundColor="rgba(228, 209, 188, 0.8)" > <CardBody> <Flex> <CardIcon /> <Spacer /> <CardIcon /> </Flex> <Image padding="10px" fallbackSrc={placeholder}></Image> </CardBody> <CardFooter> <DescriptionText /> </CardFooter> </Card> ); }; export default PlayingCard;
Hooks与服务代码
useCards.ts
import useData from "./useData"; export interface GauntletCard { id: number; title: string; } const useCards = () => useData<GauntletCard>('cards/?format=json'); export default useCards;
useData.ts
import { useEffect, useState } from "react"; import { AxiosRequestConfig, CanceledError } from "axios"; import apiClient from "../services/api-client"; interface FetchReponse<T> { count: number; results: T[]; } const useData = <T>(endpoint: string, requestConfig?: AxiosRequestConfig, deps?: any[]) => { const [data, setData] = useState<T[]>([]); const [error, setError] = useState(""); const [isLoading, setLoading] = useState(false) useEffect(() => { const controller = new AbortController(); setLoading(true); apiClient .get<FetchReponse<T>>(endpoint, { signal: controller.signal, ...requestConfig }) .then((res) => { setData(res.data.results); setLoading(false); }) .catch((err) => { if (err instanceof CanceledError) return; setError(err.message); setLoading(false); }); return () => controller.abort(); }, deps ? [...deps] : []); return { data, error, isLoading }; } export default useData;
api-client.ts代码
import axios from "axios"; export default axios.create({ baseURL: 'http://127.0.0.1:8000/', // TEMP: development server })
疑问解答与排查方案
疑问1:浏览器能正常访问接口是否就可以确定问题出在前端?
不一定100%确定,但结合Network面板能看到有效响应数据的情况,大概率是前端数据处理逻辑存在问题。不过仍需排除以下边缘情况:
- 前端请求的接口URL与浏览器访问的是否完全一致(比如是否有额外的参数、路径差异)
- 跨域问题:虽然Network能看到响应,但如果后端未正确配置CORS,可能存在请求被拦截的情况(不过你能看到payload,此概率较低)
疑问2:React StrictMode下两次渲染和请求,第一次报错是否正常?
这是正常现象。StrictMode会在开发环境下触发组件的两次渲染、useEffect的两次执行,第一次执行的useEffect会被cleanup函数(controller.abort())中止请求,所以会出现“Failed to load response data”的报错,第二次请求会正常完成并返回数据。这个现象不会导致data始终为空,因为第二次请求成功后会更新state。
前端排查重点
检查后端响应结构与
FetchReponse接口是否匹配
在useData.ts的then回调中添加日志,打印res.data,确认后端返回的JSON是否包含results字段:.then((res) => { console.log('后端响应数据:', res.data); setData(res.data.results); setLoading(false); })如果后端直接返回数组(而非
{count: number, results: T[]}结构),那么res.data.results会是undefined,setData不会改变初始的空数组,这是最可能的原因。此时需要修改useData的逻辑:// 如果后端直接返回数组 .then((res) => { setData(res.data); setLoading(false); })确认组件是否能接收到更新后的
data
在PlayingCard组件中添加日志,跟踪data的变化:const { data, isLoading, error } = useCards(); console.log('组件当前data:', data); console.log('加载状态:', isLoading); console.log('错误信息:', error);如果
isLoading变为false后data仍为空,说明后端返回的results本身是空数组,或者setData的逻辑有误。检查useEffect的依赖是否正确
useCards调用useData时未传递依赖数组,useData的useEffect依赖为空数组,这意味着只会在组件挂载时执行一次请求(StrictMode下两次),如果后续需要重新请求,需补充依赖参数,但当前问题与此无关。
内容的提问来源于stack exchange,提问作者user07773

