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

React中HeatMap组件空白报错:Uncaught TypeError读取length属性失败

HeatMapTable组件渲染空白并抛出TypeError错误

我开发的React组件HeatMapTable渲染时页面空白,控制台报错:Uncaught TypeError: Cannot read properties of undefined (reading 'length')。


组件代码

import React, { useState, useEffect } from "react";
import ClinicalRoom from '../images/ClinicalRoom.jpg'
import data3 from "../data.json"
import green from "../images/green.png"
import red from "../images/red.png"

export default function HeatMapTable({ list, colNames, room, height='auto'}) {
  const [count, setCount] = useState(0)
  const [backendData, setBackendData] = useState([{}])
   colNames = ["", "", "",""]
   
   useEffect(() => {
    fetch("/heatmapapi").then(
      response => response.json()
    ).then(
      data => {
        setBackendData(data)
      }
    )
  }, [count] ) 
  list = backendData[count].mapped
  room = backendData[count].room
  console.log(list)
  return (
    <div className="theme1">
      <h1>Heat Map</h1>
      <p1 >Room: {room}</p1>
      <button onClick={() => setCount(count - 1)}>Prev</button>
      <button onClick={() => setCount(count === 9 ? console.log("no more"): count+1)}>Next</button>
      <div className="layer"> 
        {list.length > 0 && (
          <table cellSpacing="0" style={{ height: height}}>
            <thead>
              <tr>
                {colNames.map((headerItem, index) => (
                  <th key={index}>
                    {headerItem.toUpperCase()}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {Object.values(list).map((obj, index) => (
              <tr key={index}>
                {Object.values(obj).map((value, index2) => (

                  <td  key={index2}>
                    {value === 0 ? <img className="map"src={green}/> : <img className="map" src={red}/>}
                    </td>
                ))}
              </tr>
              ))}
            </tbody>
          </table>
         )}
         <img className="room" src={ClinicalRoom}></img>
      </div>
    </div>
  )
}

路由配置(App.js)

<Route path ="/heatmap" element={< HeatMap />} />

后端接口代码(server.js)

const data2 = require("../client/src/data.json")

app.get("/heatmapapi", (req, res) => {
res.json(data2)})

data.json数据格式

{
  "room": "101",
  "mapped": [
    {"1":0, "2":1 ,"3":0, "4":1},
    {"1":0, "2":0, "3":0, "4":0},
    {"1":0, "2":0, "3":0, "4":0},
    {"1":0, "2":1, "3":0, "4":0},
    {"1":0, "2":0, "3":0, "4":0}
  ],
  "staffnum": 1
}

临时可行操作

按以下步骤操作后组件可正常运行:

  • 启动程序;
  • 将list = backendData[count].mapped改为list = backendData,room = backendData[count].room改为room = backendData.room;
  • 保存并刷新页面;
  • 改回原代码list = backendData[count].mapped、room = backendData[count].room;
  • 保存并刷新页面。

问题原因分析

  1. 数据结构不匹配:后端返回的是单个对象,但组件初始将backendData设为[{}](数组),且后续试图通过数组索引backendData[count]访问数据。当fetch完成后,backendData被设置为单个对象,此时backendData[0]等价于访问对象的0属性(不存在),导致backendData[count]为undefined,进而访问.mapped时得到undefined,最终触发list.length的读取错误。
  2. 初始渲染无数据校验:组件在初始渲染时(fetch尚未完成),backendData还是初始的[{}],backendData[0].mapped同样为undefined,直接访问list.length会抛出错误。

解决办法

方案1:调整后端返回数据为数组(适配多房间切换需求)

如果需要切换多个房间数据,修改server.js将返回数据包装为数组:

// 包装单个对象为数组,或直接使用包含多房间对象的数组
app.get("/heatmapapi", (req, res) => {
  res.json([data2])
})

方案2:修改组件代码适配单个对象数据(单房间展示)

如果仅需展示单个房间数据,调整组件逻辑移除数组索引访问:

export default function HeatMapTable({ list, colNames, room, height='auto'}) {
  const [count, setCount] = useState(0)
  // 初始状态设为空对象而非数组
  const [backendData, setBackendData] = useState({})
  colNames = ["", "", "",""]
  
  useEffect(() => {
    fetch("/heatmapapi").then(
      response => response.json()
    ).then(
      data => {
        setBackendData(data)
      }
    )
  }, [] ) // 仅组件挂载时请求一次,无需依赖count
  
  // 添加可选链和空值合并,避免数据未加载时报错
  const list = backendData?.mapped || []
  const room = backendData?.room || ""
  
  return (
    <div className="theme1">
      <h1>Heat Map</h1>
      <p1 >Room: {room}</p1>
      {/* 无需切换房间可移除Prev/Next按钮 */}
      <button onClick={() => setCount(count - 1)}>Prev</button>
      <button onClick={() => setCount(count === 9 ? console.log("no more"): count+1)}>Next</button>
      <div className="layer"> 
        {list.length > 0 && (
          <table cellSpacing="0" style={{ height: height}}>
            <thead>
              <tr>
                {colNames.map((headerItem, index) => (
                  <th key={index}>
                    {headerItem.toUpperCase()}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {/* mapped本身是数组,无需Object.values */}
              {list.map((obj, index) => (
              <tr key={index}>
                {Object.values(obj).map((value, index2) => (
                  <td key={index2}>
                    {value === 0 ? <img className="map"src={green}/> : <img className="map" src={red}/>}
                  </td>
                ))}
              </tr>
              ))}
            </tbody>
          </table>
         )}
         <img className="room" src={ClinicalRoom}></img>
      </div>
    </div>
  )
}

通用优化:添加数据校验

无论采用哪种方案,都应在访问嵌套属性时添加可选链操作符(?.)和空值合并(||),避免数据未加载或结构异常导致报错:

// 替代直接赋值的方式,确保读取安全
const currentData = backendData[count] || {}
const list = currentData?.mapped || []
const room = currentData?.room || ""

这样即使backendData[count]不存在,也能保证list是一个空数组,不会触发length读取错误。


内容的提问来源于stack exchange,提问作者Metric Melon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 08:10:30