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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 17:10:25