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.
相关产品推荐
相关产品推荐

