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

React Table触发Maximum update depth exceeded错误求助

React Table 无限更新深度错误排查与解决

问题场景

首次渲染从API获取数据的表格正常,但使用SortBy功能或触发筛选重新调用API时,出现以下无限循环更新错误:

react-dom.development.js:27292 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at checkForNestedUpdates (react-dom.development.js:27292:1)
    at scheduleUpdateOnFiber (react-dom.development.js:25475:1)
    at dispatchReducerAction (react-dom.development.js:17452:1)
    at react-table.development.js:944:1
    at react-table.development.js:253:1
    at commitHookEffectListMount (react-dom.development.js:23150:1)
    at commitLayoutEffectOnFiber (react-dom.development.js:23268:1)
    at commitLayoutMountEffects_complete (react-dom.development.js:24688:1)
    at commitLayoutEffects_begin (react-dom.development.js:24674:1)
    at commitLayoutEffects (react-dom.development.js:24612:1)

相关组件代码

TableAnime 组件

const TableAnime = () => {

  const [animes, setAnimes] = useState({});
  const [loadingData, setLoadingData] = useState(true);
  const [endpoint, setEndpoint] = useState('/anime?page[limit]=10&page[offset]=0')

  const getAnimes = async () => {
    const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
    setAnimes(response.data);
    setLoadingData(false);
  }

  useEffect(() => {
    getAnimes()
  }, []);

  const handleForm = async () => {
    const results = await axios.get(`https://kitsu.io/api/edge/anime`);
    setAnimes(results.data)
    console.log(animes)
  }
    
  const data = useMemo(() => (animes.data), [animes.data]);

  
  return (
    <div>
      <Form handleForm={handleForm}/>
      <h1>Catalogue</h1>
      {loadingData ? 
        (
          <p>Loading Please wait...</p>
        ) : (
        
          <Table animes={animes.data} />        
        )
      }
    </div>
  );
}

export default TableAnime;

Table 组件

import { useTable } from "react-table";
import { useMemo } from "react";
import { Link } from 'react-router-dom';
import moment from "moment"

import RowItem from "./RowItem";
import HeaderItem from "./HeaderItem";


const Table = ({animes}) => {
  
  const columns = useMemo(() => [ 
    { Header: "Titre", accessor: "attributes.canonicalTitle"},
    { Header: "Titre Japonais", accessor: "attributes.titles.ja_jp"},
    { Header: "Age recommandé", accessor: "attributes.ageRatingGuide"},
    { Header: "Date de sortie", accessor: d => moment(d.attributes.startDate).format("DD/MM/YYYY")},
    { Header: "Rang", accessor: "attributes.popularityRank"},
    { Header: " ", accessor: d => <Link to={`/anime/${d.id}`}>Voir les détails</Link>},
  ]);

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable({columns, data: animes})

  return (
    <>
      <table {...getTableProps()}>
        <thead>
        {headerGroups.map(headerGroup => (
            <HeaderItem headerGroup={headerGroup} key={Date.now()}/>
        ))}
        </thead>
        <tbody {...getTableBodyProps()}>
        {rows.map(row => {
          prepareRow(row)
          return (
            <RowItem key={(Date.now()*Math.random())} row={row} />
          )
        })}
        </tbody>
      </table> 
    </>
  );
}

export default Table;

RowItem 组件

const RowItem = ({row}) => {
  return (
    <tr {...row.getRowProps()}>
      {row.cells.map(cell => {
        return (
          <td {...cell.getCellProps() }>
            {cell.render('Cell')}
          </td>
        )
      })}
    </tr>
  );
}

export default RowItem

HeaderItem 组件

const HeaderItem = ({headerGroup}) => (
  <tr {...headerGroup.getHeaderGroupProps()}>
    {headerGroup.headers.map(column => (
      <th {...column.getHeaderProps()} >
        {column.render('Header')}
      </th>
    ))}
  </tr>
);

export default HeaderItem ;

问题根源与修复方案

1. useMemo 未设置依赖数组导致 columns 反复重建

你的Table组件中,columns使用了useMemo但没有提供依赖数组,这会导致每次组件渲染时都会重新创建一个全新的columns数组。React Table内部会检测到columns变化,触发表格重新计算状态,进而引发组件更新,形成无限循环。

修复:给useMemo添加空依赖数组

const columns = useMemo(() => [ 
  { Header: "Titre", accessor: "attributes.canonicalTitle"},
  { Header: "Titre Japonais", accessor: "attributes.titles.ja_jp"},
  { Header: "Age recommandé", accessor: "attributes.ageRatingGuide"},
  { Header: "Date de sortie", accessor: d => moment(d.attributes.startDate).format("DD/MM/YYYY")},
  { Header: "Rang", accessor: "attributes.popularityRank"},
  { Header: " ", accessor: d => <Link to={`/anime/${d.id}`}>Voir les détails</Link>},
], []); // 这里添加空依赖数组,确保columns只初始化一次

2. 使用不稳定的key值导致组件反复卸载重建

你在HeaderItem和RowItem中使用了Date.now()或随机数作为key,这些值每次渲染都会变化,React会认为这些是全新的组件,反复执行卸载和重建操作,触发不必要的更新,加剧无限循环问题。

修复:使用稳定的唯一标识作为key

  • 对于HeaderItem,使用headerGroup.id(React Table内置的稳定标识):
{headerGroups.map(headerGroup => (
  <HeaderItem headerGroup={headerGroup} key={headerGroup.id}/>
))}
  • 对于RowItem,使用row.id(数据本身的唯一ID):
{rows.map(row => {
  prepareRow(row)
  return (
    <RowItem key={row.id} row={row} />
  )
})}

3. 额外优化:handleForm函数的异步日志问题

handleForm中console.log(animes)无法获取到最新的animes状态,因为setAnimes是异步操作。如果需要查看更新后的数据,建议用useEffect监听animes变化:

useEffect(() => {
  console.log(animes);
}, [animes]);

4. 可选优化:监听endpoint变化自动刷新数据

如果后续需要通过修改endpoint实现分页/筛选,建议把endpoint加入useEffect的依赖数组,这样endpoint变化时自动重新请求数据:

useEffect(() => {
  getAnimes()
}, [endpoint]); // 添加endpoint到依赖数组

内容的提问来源于stack exchange,提问作者Morgane G.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:15:27