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
相关产品推荐
相关产品推荐

