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

React表格新增行后如何将光标定位到该行?

表格新增行后光标定位失效的解决方案

我有一个handleSaveCell函数,功能是用户输入条码后从API拉取数据填充表格单元格,校验必填字段通过后自动新增一行。但尝试用useRef和scrollIntoView实现新增行的光标定位时,完全没效果也没报错,求可行方案。

函数代码如下:

const handleSaveCell = async (rowIndex, columnId, value) => {
  const updatedTableData = [...tableData];
  updatedTableData[rowIndex][columnId] = value;

  // 用户离开Barcode单元格时调用接口拉取数据
  if (columnId === "Barcode" && value.trim() !== "") {
    try {
      const response = await fetchData(value.trim());
      const itemData = response.length > 0 ? response[0] : {};

      // 用接口返回数据更新当前行
      updatedTableData[rowIndex] = {
        ...updatedTableData[rowIndex],
        ItemName: itemData?.ItemName || "",
        HSN: itemData?.HSN || "",
        Size: itemData?.Size || "",
        SPrice: itemData?.SPrice || "",
        Qty: 1, // 默认数量设为1
        GSTInclusive: itemData?.GSTInclusive || "",
        CGST: itemData?.CGST || "",
        SGST: itemData?.SGST || "",
        IGST: itemData?.IGST || "",
        TaxAmt: itemData?.SPrice || "",
        Amount: itemData?.SPrice || "", // 初始金额等于售价
      };

      const barcodeVal = updatedTableData[rowIndex]["Barcode"];
      const itemNameVal = itemData?.ItemName || "";
      const qtyVal = String(updatedTableData[rowIndex]["Qty"]).trim();
      const amountVal = String(updatedTableData[rowIndex]["Amount"]).trim();

      // 必填字段校验通过后新增一行
      if (
        barcodeVal.trim() !== "" &&
        itemNameVal.trim() !== "" &&
        qtyVal.trim() !== "" &&
        amountVal.trim() !== ""
      ) {
        updatedTableData.push(
          columns.reduce((obj, column) => {
            obj[column.accessorKey] = "";
            return obj;
          }, {})
        );
      }
    } catch (error) {
      console.error(error);
    }
  } else if (columnId === "Qty") {
    // 数量变化时更新税额
    const qty = parseFloat(value) || 0;
    const sprice = parseFloat(updatedTableData[rowIndex]["SPrice"]) || 0;
    updatedTableData[rowIndex]["TaxAmt"] = qty * sprice;
  }

  setTableData(updatedTableData);

  const nonEmptyTableData = updatedTableData.filter((rowData) => {
    return Object.values(rowData).some((value) => value !== "");
  });

  sendTableData(nonEmptyTableData);
};

问题根源

React状态更新是异步的,你在setTableData后直接调用定位逻辑时,DOM还没完成渲染,新增的行元素根本不存在,自然没效果。另外如果用的是第三方表格组件(比如react-table),直接用useRef可能无法正确获取行节点。

可行解决方案

1. 用useEffect监听表格数据变化,DOM更新后执行定位

新增useEffect监听tableData的长度变化,当新增行后再执行滚动和光标聚焦:

import { useEffect } from 'react';

useEffect(() => {
  // 仅当表格行数增加时执行
  if (tableData.length > 0) {
    const newRowIndex = tableData.length - 1;
    // 通过id获取新增行(需要渲染时给每行加唯一id)
    const newRow = document.getElementById(`table-row-${newRowIndex}`);
    if (newRow) {
      // 滚动到新增行
      newRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      // 聚焦到该行的Barcode输入框(替换成你的输入框选择器)
      const barcodeInput = newRow.querySelector('[name="Barcode"]');
      barcodeInput?.focus();
    }
  }
}, [tableData]);

2. 给表格行添加可识别的唯一id

渲染表格行时,给每行加上索引对应的唯一id:

{tableData.map((row, index) => (
  <tr key={index} id={`table-row-${index}`}>
    <td>
      <input
        name="Barcode"
        value={row.Barcode}
        onChange={(e) => handleSaveCell(index, "Barcode", e.target.value)}
      />
    </td>
    {/* 其他列渲染逻辑 */}
  </tr>
))}

3. 第三方表格组件(如react-table)适配方案

如果用react-table,可以通过getRowProps给行存储ref:

import { useTable } from 'react-table';
import { useRef } from 'react';

const { getTableProps, getTableBodyProps, rows, prepareRow } = useTable({
  columns,
  data: tableData,
});

// 存储所有行的ref
const rowRefs = useRef([]);

// 渲染行时保存ref
{rows.map((row, index) => {
  prepareRow(row);
  return (
    <tr 
      {...row.getRowProps()} 
      ref={(el) => (rowRefs.current[index] = el)}
    >
      {row.cells.map((cell) => (
        <td {...cell.getCellProps()}>
          {cell.render('Cell')}
        </td>
      ))}
    </tr>
  );
})}

然后在useEffect中调用定位逻辑:

useEffect(() => {
  const lastRowIndex = rows.length - 1;
  const lastRow = rowRefs.current[lastRowIndex];
  if (lastRow) {
    lastRow.scrollIntoView({ behavior: 'smooth' });
    // 聚焦到Barcode单元格的输入框(替换成你的选择器)
    const barcodeInput = lastRow.querySelector('[data-column="Barcode"] input');
    barcodeInput?.focus();
  }
}, [rows]);

4. 利用状态更新回调(可选)

如果不想用useEffect,可以用setTableData的回调函数(确保状态更新后执行),加小延迟等待DOM渲染完成:

setTableData(updatedTableData, () => {
  setTimeout(() => {
    const newRowIndex = updatedTableData.length - 1;
    const newRow = document.getElementById(`table-row-${newRowIndex}`);
    if (newRow) {
      newRow.scrollIntoView();
      const barcodeInput = newRow.querySelector('[name="Barcode"]');
      barcodeInput?.focus();
    }
  }, 100);
});

内容的提问来源于stack exchange,提问作者Ayush Mishra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 16:53:08