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; - 保存并刷新页面。
问题原因分析
- 数据结构不匹配:后端返回的是单个对象,但组件初始将
backendData设为[{}](数组),且后续试图通过数组索引backendData[count]访问数据。当fetch完成后,backendData被设置为单个对象,此时backendData[0]等价于访问对象的0属性(不存在),导致backendData[count]为undefined,进而访问.mapped时得到undefined,最终触发list.length的读取错误。 - 初始渲染无数据校验:组件在初始渲染时(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
相关产品推荐
相关产品推荐

