React组件异步获取图片详情,页面导航时触发TypeError错误求助
问题成因与解决方案
报错成因
- 重试逻辑未随组件卸载终止:组件卸载时,你仅取消了当前正在进行的API请求,但之前通过
setTimeout调度的重试请求仍会在1秒后执行。此时组件已卸载,后续发起的请求或状态更新操作会触发异常。 - Axios响应解构错误:你直接从
axios.get的返回值中解构result,但Axios的响应对象本身并不包含result属性——接口返回的数据通常在响应对象的data字段中。这会导致result为undefined,访问result?.data自然会抛出Cannot read properties of undefined (reading 'data')错误。 - 取消请求的错误未区分处理:组件卸载调用
source.cancel()后,正在进行的请求会进入catch块,但你未判断该错误是否为请求取消导致的Axios专属错误,直接打印错误日志,进而暴露了不必要的报错信息。
修复后的代码
import { useState, useEffect, useRef } from 'react'; import axios from 'axios'; const GridCard = ({ imageURL, id }) => { const [imageDetail, setImageDetail] = useState(imageURL); const source = axios.CancelToken.source(); const timeoutRef = useRef(null); const isMounted = useRef(true); const getImageDetails = async () => { if (!isMounted.current) return; // 组件已卸载则终止执行 try { // 正确解构Axios响应:接口返回数据在response.data中 const response = await axios.get(`documents/${id}`, { cancelToken: source.token }); // 假设接口返回结构为 { result: { data: { thumbViewLink } } },请根据实际接口调整 const { result } = response.data; if (result?.data?.thumbViewLink === null) { // 保存定时器ID,方便后续清除 timeoutRef.current = setTimeout(getImageDetails, 1000); } else { setImageDetail(result?.data?.thumbViewLink); } } catch (error) { // 仅在非主动取消请求的情况下打印错误 if (!axios.isCancel(error)) { console.error("Error fetching image details", error); } } }; useEffect(() => { if (imageDetail === null) { getImageDetails(); } return () => { isMounted.current = false; // 标记组件已卸载 source.cancel("Component unmounted"); // 取消当前请求 if (timeoutRef.current) { clearTimeout(timeoutRef.current); // 清除重试定时器 } }; }, [imageDetail, id]); // 图片展示渲染逻辑 return <div>{/* 此处添加图片展示代码 */}</div>; }; export default GridCard;
关键修复点
- 跟踪组件挂载状态:用
useRef创建isMounted标记,在getImageDetails开头检查组件是否仍挂载,避免卸载后执行状态更新或请求操作。 - 清除重试定时器:通过
useRef保存setTimeout的ID,在组件卸载的清理函数中清除定时器,终止重试逻辑。 - 修正响应解构:正确获取Axios响应中的接口数据,根据实际接口返回结构调整解构逻辑。
- 区分取消请求错误:通过
axios.isCancel(error)判断是否为主动取消的请求,避免打印无关错误日志。
内容的提问来源于stack exchange,提问作者Sachin Jain
相关产品推荐
相关产品推荐

