使用MaterialReactTable虚拟表格时出现最大更新深度超限问题
问题:MaterialReactTable虚拟表格列宽计算导致无限重渲染错误
我用MaterialReactTable构建虚拟表格,尝试根据内容自动设置列宽。调用自定义的GetColumnWidth函数计算列宽后,在表格内导航时触发以下错误:
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.
说明:GetColumnWidth内部没有调用setState,按逻辑不会引发无限重渲染,但将函数调用替换为固定数值(如20、42)后,表格可正常运行。
相关代码片段
// format : [{..}, {..}, ...] const copyData = myInputTable; // Return a dictionary with the form -> columnKey: columnwidth, const getAllColumnWidth = () => { const widthDict = {}; for (const key in copyData[0]){ widthDict[key] = GetColumnWidth(copyData, key); // Here is the issue } return widthDict; } // Data is the full table -> [{..}, {..}, ...] // key is the key of the column concerned (1 in each object of data) // The goal of the function is to get the longest string's length of the column function GetColumnWidth(data, key){ if (data == undefined || key == undefined) { return 42; } const longChar = "ABCDEFGHIJKLNOPQRSTUVXYZ"; var i = 0; var maxLength = 0; for (const line of data){ var len = 0; if (line[key] === null || line[key] === undefined) { continue; } for (const lettre of line[key].toString()) { // Here I'm just defining the different sizes depending on the character if (longChar.indexOf(lettre) !== -1) { len += 0.26; } if (lettre === 'W') { len += 1; } if(lettre === 'M') { len += 0.85; } if (lettre === 'm') { len += 0.80; } if (lettre === 'w') { len += 0.66 } if (lettre === ' ') { len += 0.55; continue; } len++; } // Save the length if it's the longest string ever seen in the column if (len > maxLength) { maxLength = len; } } const coeff = 10; return 20 + maxLength * coeff; }
原因分析
- 重复计算触发重渲染循环:
getAllColumnWidth和GetColumnWidth在组件渲染阶段被反复调用。每次表格导航(滚动、分页等),MaterialReactTable会重新计算列属性,而直接在列配置中调用这些函数,会导致每次渲染都生成新的宽度对象,触发组件重新渲染,进而再次调用函数,形成无限循环。 - 引用类型不稳定:
getAllColumnWidth每次调用都会返回一个全新的widthDict对象,React会判定该属性发生变化,从而触发表格组件的更新。
解决办法
方法1:用useMemo缓存计算结果
将宽度计算结果缓存,仅当依赖的myInputTable变化时才重新计算,避免每次渲染生成新对象:
import { useMemo } from 'react'; // 缓存宽度计算结果,依赖myInputTable变化时才重新计算 const columnWidths = useMemo(() => { const widthDict = {}; if (!myInputTable || myInputTable.length === 0) return widthDict; for (const key in myInputTable[0]){ widthDict[key] = GetColumnWidth(myInputTable, key); } return widthDict; }, [myInputTable]); // 依赖数组:仅myInputTable变化时重新执行
之后在列配置中直接使用columnWidths[key]即可。
方法2:用useEffect+状态存储计算结果
把列宽计算移到数据更新时执行,结果存入状态:
import { useState, useEffect } from 'react'; const [columnWidths, setColumnWidths] = useState({}); useEffect(() => { const widthDict = {}; if (!myInputTable || myInputTable.length === 0) { setColumnWidths({}); return; } for (const key in myInputTable[0]){ widthDict[key] = GetColumnWidth(myInputTable, key); } setColumnWidths(widthDict); }, [myInputTable]);
方法3:优化GetColumnWidth性能
该函数计算量随数据量增长而变大,可进一步缓存单列的计算结果,或者在数据加载完成后一次性计算所有列宽,减少重复计算开销。
内容的提问来源于stack exchange,提问作者Tom Pauwels
相关产品推荐
相关产品推荐

