React传递参数时useFetch自定义Hook失效问题求助
问题分析与修复方案
核心问题1:Fetch Aborted 错误
Calendar组件中每次渲染都会创建新的FormData实例,而useFetch的useEffect依赖了formData(引用类型)。由于每次渲染生成的FormData都是新对象,会不断触发useEffect执行,旧请求被AbortController终止,最终出现"Fetch aborted"错误。
同时,App组件中apiData是异步加载的,初始值为null,Calendar组件初始化时apiData未就绪,直接创建FormData会导致apiData.schoolGroup等属性报错,间接引发组件重复渲染。
核心问题2:第三方API POST请求失效
当前useFetch默认强制解析响应为JSON,如果第三方API返回的不是JSON格式,会抛出错误导致请求失败。另外,需确认第三方API是否支持multipart/form-data类型的请求(FormData默认触发该类型)。
修复步骤
1. 修复Calendar组件的FormData重复创建问题
使用useMemo缓存FormData,仅当依赖的user或apiData变化时才重新生成,避免不必要的渲染触发:
import React, { useMemo } from 'react' import useFetch from '../Components/useFetch'; const Calendar = ({user, apiData}) => { // 仅当user或apiData变化时,才创建新的FormData const apiFormData = useMemo(() => { // 确保user和apiData都已加载完成 if (!user || !apiData) return null; const formData = new FormData(); formData.append('school_group', apiData.schoolGroup); formData.append('db', apiData.db); formData.append('student_idx', user.calendarId); return formData; }, [user, apiData]); // 只有当apiFormData存在时,才发起请求 const {data: calendar, error: calError, isPending: calIsPending} = useFetch( apiFormData ? 'https://MY-API-LINK' : null, apiFormData ); return ( <div> {calError && <div>{calError}</div>} {calIsPending && <div>Loading...</div>} {calendar && ( <div> <h2>Calendar</h2> {console.log(calendar)} </div> )} </div> ) } export default Calendar
2. 优化useFetch,兼容非JSON响应
修改useFetch,增加响应类型判断,避免强制解析JSON导致的错误:
import { useState, useEffect } from 'react'; const useFetch = (url, formData = null) => { const [data, setData] = useState(null); const [isPending, setIsPending] = useState(true); const [error, setError] = useState(null); useEffect(() => { // 如果url为空,直接终止逻辑 if (!url) { setIsPending(false); return; } const abortCont = new AbortController(); const fetchData = async () => { try { const fetchOptions = { method: 'GET', signal: abortCont.signal, }; if (formData) { fetchOptions.method = 'POST'; fetchOptions.body = formData; // FormData会自动设置Content-Type为multipart/form-data,无需手动添加 } const response = await fetch(url, fetchOptions); if (!response.ok) { throw Error(`请求失败:${response.status} ${response.statusText}`); } // 尝试解析JSON,如果失败则返回文本 let responseData; try { responseData = await response.json(); } catch { responseData = await response.text(); } setData(responseData); setIsPending(false); setError(null); } catch (err) { if (err.name !== 'AbortError') { setIsPending(false); setError(err.message); } } }; fetchData(); return () => abortCont.abort(); }, [url, formData]); return { data, isPending, error }; }; export default useFetch;
3. 补充App组件的加载状态处理
确保Calendar组件仅在apiData就绪后才接收有效数据,避免初始渲染时的错误:
// 在App.js的Calendar路由中,增加apiData的加载判断 <Route path="/calendar" element={ apiIsPending ? <div>Loading API config...</div> : <Calendar user={user} apiData={apiData}/> } />
额外排查点
- 确认第三方API是否允许跨域请求(可在浏览器控制台Network面板查看CORS错误),若存在跨域问题,需通过后端代理转发请求。
- 检查FormData中的参数名是否与API要求完全一致(比如是否是
school_group还是schoolGroup)。
内容的提问来源于stack exchange,提问作者Furkan Karaoglu
相关产品推荐
相关产品推荐

