React-Table无法渲染API返回的变量数据,API返回数据已验证
问题修复:React-Table 无法渲染API数据且报错的解决方法
核心问题分析
- 直接将异步函数
populateTable()赋值给setDATA,导致DATA被设置为Promise对象,而非API返回的数组 data变量未通过useMemo稳定处理,且存在未定义的边界情况,导致useTable接收undefined数据触发forEach报错- 状态更新逻辑错误,未等待API请求完成就更新状态
- API请求URL存在语法错误(多余的
{})
分步修复
1. 修复API调用与状态更新逻辑
HandleClick中直接传递异步函数给setDATA是错误的,需等待请求完成后再设置实际数据:
const HandleClick = async () => { const apiData = await populateTable(); if (apiData) { // 确保数据有效再更新状态 setDATA(apiData); } ToggleShown(); }
2. 用useMemo稳定处理数据转换
删除原来零散的data变量声明,改用useMemo统一处理数据转换,同时添加边界保障:
const data = useMemo(() => { const rawData = DATA[0]; if (!rawData) return []; // 数据不存在时返回空数组,避免useTable报错 return Object.keys(rawData).map(key => rawData[key]); }, [DATA]);
3. 确保useTable始终接收有效数据
给useTable的data参数添加双重保险,保证永远是数组类型:
const tableInstance = useTable({ columns, data: data || [] });
4. 修复API请求URL的语法错误
去掉URL中多余的{},保证请求地址正确:
// 错误写法 let targetURI = `http://localhost:8000/api/calculateProfitPerHourOf{}?buildingName=${encodedName}&...` // 修复后 let targetURI = `http://localhost:8000/api/calculateProfitPerHourOf?buildingName=${encodedName}&buildingLevel=${buildingLevel}&productionModifierPercentage=${productionModifierPercentage}&administrationCostPercentage=${adminCostPercentage}`
5. 优化状态切换逻辑
使用函数式更新切换shown状态,避免异步状态依赖问题:
const ToggleShown = () => { setShown(prev => !prev); }
完整修复后的callApi.js代码
import React, { useState, useEffect, useMemo } from "react"; import { COLUMNS } from "./columns"; import { useTable } from "react-table"; import axios from 'axios' export const CallApi = () => { const [buildingName, setBuildingName] = useState(''); const [buildingLevel, setBuildingLevel] = useState(''); const [productionModifierPercentage, setProductionModifier] = useState(''); const [adminCostPercentage, setAdminCostPercentage] = useState(''); const [phase, setPhase] = useState(''); const [DATA, setDATA] = useState([{ "1": { "itemName": "Energy research", "profitPerHour": 821.6746356364301 } }]); const [shown, setShown] = useState(false); const columns = useMemo(() => COLUMNS, []); // 统一处理数据转换,保证返回数组类型 const data = useMemo(() => { const rawData = DATA[0]; if (!rawData) return []; return Object.keys(rawData).map(key => rawData[key]); }, [DATA]); const tableInstance = useTable({ columns, data: data || [] }); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = tableInstance; const populateTable = async () => { try{ let encodedName = encodeURI(buildingName) let targetURI = `http://localhost:8000/api/calculateProfitPerHourOf?buildingName=${encodedName}&buildingLevel=${buildingLevel}&productionModifierPercentage=${productionModifierPercentage}&administrationCostPercentage=${adminCostPercentage}` let res = await axios.get(targetURI); return res.data; } catch(e) { console.error('API请求失败:', e); return null; } } useEffect(() => { console.log('当前DATA状态:', JSON.stringify(DATA)) }, [DATA]); const ToggleShown = () => { setShown(prev => !prev); } const HandleClick = async () => { const apiData = await populateTable(); if (apiData) { setDATA(apiData); } ToggleShown(); } const whatIsSetShown = () => { console.log('Stringified DATA:', JSON.stringify(DATA)) console.log('转换后的数据:', data) } return( <div> <input type="text" name="buildingName" id="buildingName" placeholder="Building Name" onChange={e => setBuildingName(e.target.value)}></input> <input type="text" name="buildingLevel" id="buildingLevel" placeholder="Building Level" onChange={e => setBuildingLevel(e.target.value)}></input> <input type="text" name="productionModifier" id="productionModifier" placeholder="Production Modifier %" onChange={e => setProductionModifier(e.target.value)}></input> <input type="text" name="adminCost" id="adminCost" placeholder="Administration Cost %" onChange={e => setAdminCostPercentage(e.target.value)}></input> <input type="text" name="phase" id="phase" placeholder="Phase" onChange={e => setPhase(e.target.value)}></input> <p id='Button'> <button type='button' id="getDBinfo" className='block' onClick={HandleClick}>Get DB Info</button> <button type='button' id="getDBinfo" className='block' onClick={whatIsSetShown}>Print State</button> </p> {shown && ( <table {...getTableProps()}> <thead> {headerGroups.map((headerGroup) => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map((column) => ( <th {...column.getHeaderProps()}>{column.render("Header")}</th> ))} </tr> ))} </thead> <tbody {...getTableBodyProps()}> {rows.map((row) => { prepareRow(row); return ( <tr {...row.getRowProps()}> {row.cells.map((cell) => { return <td {...cell.getCellProps()}>{cell.render("Cell")}</td>; })} </tr> ); })} </tbody> </table> )} </div> ) };
关键说明
- 异步状态更新必须等待异步操作完成,不能直接传递Promise给
setState - 数据转换逻辑用
useMemo包裹,避免重复计算并保证数据稳定性 - 始终保证传递给
useTable的data是数组类型,哪怕是空数组,杜绝undefined引发的报错 - 状态切换优先使用函数式更新,避免异步场景下的状态依赖问题
内容的提问来源于stack exchange,提问作者Logan Pederson
相关产品推荐
相关产品推荐

