You need to enable JavaScript to run this app. react-table 7.6.3下React Hook组件输入框onChange后失焦问题求助
优惠活动
大模型
产品
解决方案
定价
更多

react-table 7.6.3下React Hook组件输入框onChange后失焦问题求助

React Table 7.6.3 可编辑表格输入失焦问题修复

问题根本原因

失焦的核心触发逻辑是:每次输入框onChange更新本地状态时,父组件触发重渲染,此时:

  • 原本内联在JSX中的columns数组会被重新生成,数组内定义的每个Cell渲染函数都会生成全新的函数引用
  • React Table 7.x会检测到columns引用变化,触发全量单元格重渲染
  • 重渲染过程中,原本的input DOM节点因为没有稳定的组件引用,被React判定为不同的节点类型,直接被卸载替换,导致输入焦点丢失

之前参考的针对v6版本的修复方案没有效果,本质是v7版本完全基于Hooks驱动渲染逻辑,和v6的渲染机制差异极大,旧方案针对DOM焦点的手动操作思路无法适配v7的组件渲染流程。

具体修复步骤

1. 保持columns定义的引用稳定性

不要把columns数组直接内联写在<MyReactTable>组件的JSX props里,需要用useMemo包裹columns定义,仅在必要的依赖(比如字段值、回调函数引用)变化时才重新生成columns数组,避免每次父组件渲染都创建全新的columns对象。

2. 将可编辑单元格拆分为独立的memo化组件

不要在Cell的渲染回调里直接写大段内联的input DOM结构,把可编辑的input封装为独立组件,用React.memo包裹,让组件仅在自身关联的props变化时才重渲染,避免无关的重渲染触发input节点重建。

3. 优化状态更新与回调函数的引用稳定性

  • 状态更新采用函数式写法,避免直接引用闭包内的旧状态值,同时减少回调函数的依赖项
  • 事件处理函数用useCallback包裹,保持函数引用稳定,减少不必要的组件重渲染

修正后的核心代码示例

import React, { useState, useMemo, useCallback } from 'react'

// 独立的memo化可编辑单元格,避免每次渲染重建DOM节点
const EditableCell = React.memo(({ value, rowId, onChange, disabled }) => {
  return (
    <div>
      <input
        style={{ color: '#627FE3' }}
        value={value}
        disabled={disabled}
        onChange={(e) => onChange(rowId, e)}
      />
    </div>
  )
})

const TableContainer = () => {
  const [fieldValues, setFieldValues] = useState({})

  // 用useCallback保持事件处理函数的引用稳定
  const handleInitialDemandChange = useCallback((rowId, e) => {
    e.persist()
    // 函数式更新状态,避免引用闭包内的旧值
    setFieldValues(prev => ({
      ...prev,
      [rowId]: e.target.value
    }))
  }, [])

  // 用useMemo保持columns引用稳定,仅在依赖变化时重新生成
  const columns = useMemo(() => [
    {
      Header: '',
      id: 'id',
      accessor: () => {},
      Cell: ({ cell }) => (
        <div style={{ color: '#627FE3', display: 'flex', alignItems: 'center' }}>
          {cell.row.canExpand ? (
            cell.row.isExpanded ? (
              <img
                alt="expand"
                src="/images/icons/expand.svg"
                {...cell.row.getToggleRowExpandedProps({
                  style: { paddingLeft: `${cell.row.depth * 2}rem` }
                })}
                height="30"
                width="30"
              />
            ) : (
              <img
                alt="collapse"
                src="/images/icons/collapse.svg"
                {...cell.row.getToggleRowExpandedProps({
                  style: { paddingLeft: `${cell.row.depth * 2}rem` }
                })}
                height="30"
                width="30"
              />
            )
          ) : null}
        </div>
      )
    },
    {
      Header: 'Item',
      accessor: 'item'
    },
    {
      Header: 'Amount Paid',
      accessor: 'amount_paid',
      Cell: ({ cell }) => (
        <p style={{ color: '#627FE3', cursor: 'pointer', margin: 0 }}>
          {cell.row.values.amount_paid}
        </p>
      )
    },
    {
      Header: 'Initial Demand',
      accessor: 'initial_demand',
      Cell: ({ cell }) => (
        <EditableCell
          value={fieldValues[cell.row.original.id] ?? cell.value}
          rowId={cell.row.original.id}
          onChange={handleInitialDemandChange}
          disabled={cell.row.original.id == 0}
        />
      )
    }
  ], [fieldValues, handleInitialDemandChange])

  return (
    <MyReactTable
      id="myReactTable"
      columns={columns}
      data={round1Summary || []}
    />
  )
}

补充说明

MyReactTable封装组件本身的逻辑无需做大幅修改,只要保证传入的columns、data props引用稳定,内部的useTable hook就会按预期的粒度触发重渲染,不会全量替换所有单元格的DOM节点。如果优化后仍有偶发失焦,可以进一步检查传入表格的data数组是否存在每次渲染都重新生成的问题,同样可以用useMemo包裹data引用,减少不必要的表格重渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 12:06:01