解决TanStack React Table输入时重渲染致输入框失焦问题
问题描述
我正在开发一个表格,其中一列可输入单价,另一列会根据输入的单价和“Quantity(数量)”列的值计算总价(总价 = 数量 × 单价)。原本在React中未使用任何库实现了该功能,但由于需要添加筛选和排序功能,决定迁移到TanStack Table。
现在遇到的问题是:在输入框中每输入一个字符,输入框就会立即失去焦点,无法完整输入内容。排查后认为原因是每输入一个字符表格就会重新渲染导致的。我已经尝试用useMemo避免重渲染,但没效果,表格还是会在输入时立即重新渲染。
表格列配置代码
const defaultColumns = [ // Accessor Column columnHelper.accessor("totalQty", { cell: (info) => info.getValue(), footer: (props) => props.column.id, header: () => <span>Quantity</span>, }), // ...other columns // Accessor Column columnHelper.accessor("unitPrice", { header: "Unit Price", footer: (props) => props.column.id, cell: (props) => ( <input type="number" className="number-input" value={props.row.original.unitPrice} onChange={(e) => { handleUnitPriceChange(props.row.index, Number(e.target.value)); }} /> ), }), // Accessor Column columnHelper.accessor( (row) => { return row.totalQty ? row.totalQty * row.unitPrice : null; }, { header: "Total Price", footer: (props) => props.column.id, cell: (props) => ( <span> {props.row.original.totalQty ? props.row.original.totalQty * props.row.original.unitPrice : null} </span> ), } ), ]; const table = useReactTable({ data, columns: defaultColumns, getCoreRowModel: getCoreRowModel(), });
单价更新函数代码
function handleUnitPriceChange(index: number, up: number | null) { const updatedPurchaseReq = { ...purchaseReq }; if (up !== null) { updatedPurchaseReq.itemList[index].unitPrice = up; updatedPurchaseReq.itemList[index].totalCost = up * updatedPurchaseReq.itemList[index].totalQty; } else { updatedPurchaseReq.itemList[index].unitPrice = 0; updatedPurchaseReq.itemList[index].totalCost = 0; } setPurchaseReq(updatedPurchaseReq); }
解决方案
1. 用useMemo缓存列配置
之前尝试useMemo未生效,核心原因是defaultColumns在组件每次渲染时都会重新创建。将列配置用useMemo包裹,确保只有依赖项变化时才重新生成:
const defaultColumns = useMemo(() => [ columnHelper.accessor("totalQty", { cell: (info) => info.getValue(), footer: (props) => props.column.id, header: "Quantity", // 直接用字符串替代匿名函数组件,减少不必要的组件重建 }), // ...other columns columnHelper.accessor("unitPrice", { header: "Unit Price", footer: (props) => props.column.id, cell: (props) => ( <input type="number" className="number-input" value={props.row.original.unitPrice} onChange={(e) => { handleUnitPriceChange(props.row.index, Number(e.target.value)); }} /> ), }), columnHelper.accessor( (row) => row.totalQty ? row.totalQty * row.unitPrice : null, { header: "Total Price", footer: (props) => props.column.id, cell: (props) => ( <span> {props.row.original.totalQty ? props.row.original.totalQty * props.row.original.unitPrice : null} </span> ), } ), ], [columnHelper]); // 仅依赖columnHelper,保证列配置的稳定性
2. 修复数据更新的不可变性问题
原更新函数中,itemList数组仍引用原对象,未正确创建新数组,导致React无法准确判断数据变化,引发不必要重渲染。修改为:
function handleUnitPriceChange(index: number, up: number | null) { setPurchaseReq(prev => { const updatedItemList = prev.itemList.map((item, idx) => { if (idx !== index) return item; const unitPrice = up ?? 0; return { ...item, unitPrice, totalCost: unitPrice * item.totalQty }; }); return { ...prev, itemList: updatedItemList }; }); }
3. 确保数据源的稳定性
如果表格数据来自purchaseReq.itemList,用useMemo缓存数据源,避免每次渲染生成新数组:
const data = useMemo(() => purchaseReq.itemList, [purchaseReq.itemList]);
内容的提问来源于stack exchange,提问作者Alwin Sanil
相关产品推荐
相关产品推荐

