You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React useFetch请求API偶发Cannot read properties of undefined报错

问题根因

你的判断方向没错,但现有代码有两个明确问题导致偶发报错、数据拉取不稳定:

  • 渲染逻辑存在边界漏洞:仅判断了data是否存在,既没处理接口请求失败、返回数组长度不足的场景,还错误地拿固定长度为5的images数组做map遍历基准。一旦接口返回慢、请求出错导致data没被正确赋值、或者接口临时返回的数组项少于5个,遍历到超出data长度的索引时,data[i]就会是undefined,读取type/count属性时直接触发"Cannot read properties of undefined"报错。
  • 自定义Hook存在请求竞态问题:没有处理组件卸载、url快速变更触发重复请求的场景,后发先至的旧请求会覆盖正确的返回数据,直接导致数据偶发异常。
修复代码

1. 修复useFetch Hook

// useFetch.js
import { useEffect, useState } from "react";
import axios from "axios";

const useFetch = (url) => {
  // data初始值设为空数组,避免初始状态为undefined
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    // 新增请求取消标记,解决竞态问题
    const abortController = new AbortController();
    const fetchData = async () => {
      setLoading(true);
      setError(null);
      try {
        const res = await axios.get(url, { signal: abortController.signal });
        setData(res.data);
      } catch (err) {
        // 主动取消的请求不需要更新错误状态
        if (!abortController.signal.aborted) {
          setError(err);
        }
      } finally {
        // 组件已卸载的话不更新loading状态,避免内存泄漏
        if (!abortController.signal.aborted) {
          setLoading(false);
        }
      }
    };
    fetchData();

    // 组件卸载、url变更时取消上一次未完成的请求
    return () => abortController.abort();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

2. 修复PropertyList组件渲染逻辑

// PropertyList.jsx
import React from "react";
import useFetch from "../../hooks/useFetch";
import "./propertyList.css";

function PropertyList() {
  const { data, loading, error } = useFetch(
    "http://localhost:3000/api/hotels/countByType"
  );

  const images = [
    "https://media-cdn.tripadvisor.com/media/photo-s/1c/d3/b2/4f/exterior-view.jpg",
    "https://www.phillyaptrentals.com/wp-content/uploads/2020/12/apartment-building-what-makes-good-apartment-building-scaled.jpg",
    "https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/01/04/c9/manduna-resort.jpg?w=900&h=-1&s=1",
    "https://images.squarespace-cdn.com/content/v1/585562bcbe659442d503893f/c3b765c0-45e3-46b3-9ff9-b4101fb30674/01.+Exotik+Villas+Bali+-+Aloui.jpg",
    "https://vancouverisland.travel/app/uploads/2021/04/wood-cabin-on-vancouver-island_BenGiesbrecht.jpg",
  ];

  // 错误场景兜底
  if (error) return <div className="pList">数据加载失败,请刷新重试</div>;

  return (
    <div className="pList">
      {loading ? (
        "Loading.."
      ) : (
        <>
          {/* 以接口返回的data为遍历基准,从根源避免索引越界 */}
          {data.map((item, i) => {
            // 数据项缺失、无对应配图时直接跳过渲染
            if (!item || !images[i]) return null;
            return (
              <div className="pListItem" key={item.type}>
                <img src={images[i]} alt={item.type} className="pListImg" />
                <div className="pListTitles">
                  <h1>{item.type}</h1>
                  <h2>
                    {item.count} {item.type}
                  </h2>
                </div>
              </div>
            );
          })}
        </>
      )}
    </div>
  );
}

export default PropertyList;
关键修复点说明
  • 给data设置初始空数组,避免初始undefined导致的判断失效
  • 加入AbortController处理请求竞态和组件卸载后的状态更新,解决内存泄漏、旧请求覆盖新数据的问题
  • 调整遍历逻辑,不再用固定长度的配图数组做遍历基准,完全以接口返回的数据长度为准,彻底解决索引越界读不到属性的问题
  • 增加错误场景兜底和单条数据合法性判断,哪怕接口返回结构异常也不会直接抛错白屏
  • 列表key从索引替换为接口返回的唯一字段type,避免React渲染列表出现状态错乱

内容的提问来源于stack exchange,提问作者Varun Nayak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:12:22