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

React Hook Form useFieldArray筛选异常:搜索'test'显示错误行

问题:React Hook Form结合useFieldArray筛选列表时显示异常

使用React Hook Form结合useFieldArray创建列表,通过搜索框实现列表筛选时功能异常:搜索“test”时,本该显示最后两行含“test”的条目,却显示了前两行。

代码示例

import React from 'react';
import { useForm, useFieldArray, Controller, useWatch } from 'react-hook-form';
import ReactDOM from 'react-dom';

let renderCount = 0;

export function App() {
  const { register, control, handleSubmit, reset, watch } = useForm({
    defaultValues: {
      test: [
        { firstName: 'Bill 1', lastName: 'Luo' },
        { firstName: 'hello2', lastName: 'Luo' },
        { firstName: 'test 3', lastName: 'Luo' },
        { firstName: 'test 4', lastName: 'Luo' },
      ],
    },
  });
  const { fields, append, prepend, remove, swap, move, insert, replace } =
    useFieldArray({
      control,
      name: 'test',
    });

  const onSubmit = data => console.log('data', data);

  const [searchQuery, setSearchQuery] = React.useState('');

  const handleSearchChange = event => {
    setSearchQuery(event.target.value);
  };

  const filteredRows = fields.filter(row =>
    row.firstName.toLowerCase().includes(searchQuery.toLowerCase())
  );

  console.log(JSON.stringify(filteredRows));
  return (
    <>
      <input onChange={handleSearchChange} value={searchQuery} />

      <form onSubmit={handleSubmit(onSubmit)}>
        <ul>
          {filteredRows.map((item, index) => {
            return (
              <li key={item.id}>
                <input
                  {...register(`test.${index}.firstName`, { required: true })}
                />

                <Controller
                  render={({ field }) => <input {...field} />}
                  name={`test.${index}.lastName`}
                  control={control}
                />
              </li>
            );
          })}
        </ul>
      </form>
    </>
  );
}

// Log to console
console.log('Hello console');

问题原因

核心错误在于使用筛选后数组的index作为表单字段的路径索引:

  • 当你对fields数组筛选得到filteredRows后,map循环的index是筛选后数组的索引(比如搜索“test”后,filteredRows有2条数据,索引为0和1)
  • 但你绑定的字段路径是test.${index}.firstName,这会指向原始fields数组的第0、1项(即“Bill 1”和“hello2”对应的字段),而非筛选出来的“test 3”、“test 4”项
  • 最终表现为:你渲染了筛选后的条目结构,但实际绑定的是原始数组前两项的表单值,所以看起来显示的是前两行。

解决方法

通过每个条目的唯一id找到它在原始fields数组中的索引,用这个原始索引来绑定表单字段路径:

{filteredRows.map((item) => {
  // 找到当前条目在原始fields数组中的索引
  const originalIndex = fields.findIndex(row => row.id === item.id);
  return (
    <li key={item.id}>
      <input
        {...register(`test.${originalIndex}.firstName`, { required: true })}
      />
      <Controller
        render={({ field }) => <input {...field} />}
        name={`test.${originalIndex}.lastName`}
        control={control}
      />
    </li>
  );
})}

这样就能确保筛选后的条目正确绑定到对应的数据字段,显示内容与筛选结果一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:23:14