React Hook Form中useFieldArray跨字段数组操作时数据临时丢失问题排查与解决
React Hook Form中useFieldArray跨字段数组操作时数据临时丢失问题排查与解决
问题原因分析
你的问题本质是UI层与Form State的同步延迟加上React Table的不稳定依赖共同导致的:
- 输入值暂存与同步延迟:你的
EditableTextCell组件用本地useState临时存储输入内容,只有在输入框失焦时才同步到React Hook Form的State中。当你操作另一个Field Array(比如添加Board Member)时,组件触发全局重新渲染,此时EditableTextCell的useEffect会监听来自Form State的旧值(还没同步本地输入的内容),把本地value重置为空,导致UI上数据“消失”。 - React Table依赖不稳定:在
SystemUsersTable中,你每次渲染都会创建新的columns数组,这会让useReactTable频繁重新计算表格模型,不仅浪费性能,还会加剧数据显示的异常。
而当你添加新的System User时,对应的Field Array发生变化,useFieldArray返回的systemUsers数组更新,触发表格重新渲染——如果之前的输入已经失焦同步到了Form State,UI就会重新读取到正确的值,这就是你看到数据“重新出现”的原因。
解决方案
我们可以通过以下几步彻底解决这个问题:
1. 移除本地状态,实时同步Form State
取消EditableTextCell中的本地useState,让输入框直接和Form State绑定,输入内容实时同步到Form State,组件重新渲染时也会直接读取最新值:
// EditableTextCell.tsx 修改后 export const EditableTextCell = ({ getValue, row: { index }, column: { id }, table, disabled = false, }: CellContext<T, string> & { disabled?: boolean }) => { // 直接从Form State获取当前值 const value = getValue(); const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { // 实时同步输入值到Form State table.options.meta?.updateData(index, id, e.target.value); }; return ( <Input value={value} onChange={handleChange} disabled={disabled} /> ); };
2. 缓存React Table的列配置,避免不必要重渲染
用useMemo缓存columns数组,确保只有在依赖变化时才更新,避免useReactTable频繁重建表格模型:
// SystemUsersTable.tsx 修改后 export const SystemUsersTable = ({ data, isEditing, onRemove, onFieldChange, }: SystemUsersTableProps) => { // 用useMemo缓存列配置,仅在isEditing变化时更新 const columns = useMemo(() => [ { accessorKey: "name", header: "Employee Name", cell: (props) => ( <EditableTextCell {...props} disabled={!isEditing} /> ), }, // ... 其他列定义 ], [isEditing]); const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), meta: { updateData: (rowIndex, columnId, value) => { onFieldChange(rowIndex, columnId, value); }, isEditing, onRemove, }, }); // ... 表格渲染逻辑 };
3. 可选:缓存Field Array数据,避免意外重渲染
虽然React Hook Form的useFieldArray返回的fields已经是memoized的,但可以给systemUsers加一层缓存,确保只有数据真的变化时才传递给表格:
// CompanyForm.tsx 修改后 const memoizedSystemUsers = useMemo(() => systemUsers, [systemUsers]); <SystemUsersTable data={memoizedSystemUsers} isEditing={isEditing} onRemove={removeSystemUser} onFieldChange={(index, field, value) => { form.setValue(`systemUsers.${index}.${field}`, value); }} />
效果验证
修改完成后:
- 你在System Users输入的内容会实时同步到Form State,不会因为操作Board Members而丢失
- 两个Field Array的操作完全独立,修改其中一个不会影响另一个的数据显示
- React Table的渲染性能也会得到提升,不会出现无意义的重新计算
内容来源于stack exchange
相关产品推荐
相关产品推荐

