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

解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:05:09