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

如何使用react-hook-form修改对象数组中的值

问题解决:React Hook Form 中更新已添加数组对象的值

使用技术

JavaScript + React、react-hook-form 和 material-ui

问题说明

我有一个多分类的列表(DatasTest),用户勾选分类下的对象时,通过react-hook-form的append方法把对象添加到数组里(这部分功能正常)。现在需要实现更新已添加对象的特定值的功能。

父组件代码(含DatasTest列表)

import { Box, Stack } from '@mui/material';
import React from 'react';
import { FormReusable, Test } from '../../reusable/FormReusable';
import { useFieldArray } from 'react-hook-form';

const users = [
  { engineer: 'user One', title: 'name One' },
  { engineer: 'user Two', title: 'name One' },
  { engineer: 'user Three', title: 'name Two' },
  { engineer: 'user Four', title: 'name Three' },
];
  

const DatasTest = {
  AssesmentDatas: [
    { name: 'name One' },
    { name: 'name Two' },
    { name: 'name Three' },
    { name: 'name Four' },
  ],
  DesignDatas: [
     { name: 'name One' },
    { name: 'name Two' },
    { name: 'name Three' },
    { name: 'name Four' },
  ],
  ImplementationDatas: [{ name: 'Name Five' }],
};
 

export const ProjectType = ({ control, watch, register, replace }) => {
  return (
       <Test
        DatasTest={DatasTest}
        control={control}
        watch={watch}
        register={register}
        users={users}
        replace={replace}
      />
      
  );
};

含复选框与输入框的子组件代码

export const Test = ({ DatasTest, control, watch, users, register }) => {
  const currentDate = new Date();

  const watchTest = watch('test');

  const { fields, append, prepend, remove, swap, move, insert, replace } =
    useFieldArray({
      control,
      name: 'test',
    });


  const handleCheckboxChange = ({ event, key, index, el }) => {
    if (event.target.checked) {
      append({
        labelName: key,
        name: el?.name,
        currentDate: currentDate,
        engineer: '',
        url: '',
      });
    } else {
      remove(index);
    }
  };

  return (
    <Box>
      {JSON.stringify(watchTest)}
      {Object.keys(DatasTest).map((key) => (
        <div key={key}>
          <h2>{key}</h2>
          <ul>
            {DatasTest[key].map((el, index) => {
              const filteredUsers = users?.filter((user) =>
                user?.title.includes(el?.name)
              );

              return (
                <Box>
                  <FormControlLabel
                    sx={{ width: '20%', margin: '1em 2em' }}
                    control={<Checkbox />}
                    onChange={(event) =>
                      handleCheckboxChange({ event, key, index, el })
                    }
                    label={el?.name}
                  />{' '}

                  <FormControl sx={{ width: '25%', margin: '1em 2em' }}>
                    <Select {...register(`test${[index]}.engineer`)}>
                      {filteredUsers?.map((user, index) => (
                        <MenuItem key={index} value={user?.engineer}>
                          {user?.engineer}
                        </MenuItem>
                      ))}
                    </Select>
                  </FormControl>{' '}
                </Box>
              );
            })}
          </ul>
        </div>
      ))}
    </Box>
  );
};

已尝试方法

我试过给FormControl里的engineer组件加register,用{...register(test${[index]}.engineer)}来更新,但没生效,数组里的对象根本没变化。


问题原因

你当前的逻辑有两个核心问题:

  1. 用DatasTest数组的索引来绑定表单字段,但这个索引和test数组的索引完全不对应(同一个名称可能在多个分类里出现,append的顺序也和原数组无关)
  2. 表单控件和未勾选的元素绑定,导致选择值时找不到对应的test数组对象

解决方案

步骤1:修正复选框的增删逻辑

取消勾选时,要根据元素的唯一标识(labelName+name)找到test数组里的对应项删除,而不是用原数组索引:

const handleCheckboxChange = ({ event, key, el }) => {
  if (event.target.checked) {
    // 避免重复添加相同项
    const exists = watchTest.some(item => item.labelName === key && item.name === el.name);
    if (!exists) {
      append({
        labelName: key,
        name: el.name,
        currentDate: currentDate,
        engineer: '',
        url: '',
      });
    }
  } else {
    // 找到test数组中匹配的元素索引并删除
    const targetIndex = watchTest.findIndex(item => item.labelName === key && item.name === el.name);
    if (targetIndex !== -1) {
      remove(targetIndex);
    }
  }
};

步骤2:拆分渲染区域,单独处理已选元素的编辑

把复选框选择区域和已选元素的编辑区域分开,确保表单控件只绑定test数组里的元素:

return (
  <Box>
    {JSON.stringify(watchTest)}
    {/* 分类复选框选择区域 */}
    {Object.keys(DatasTest).map((key) => (
      <div key={key}>
        <h2>{key}</h2>
        <ul>
          {DatasTest[key].map((el) => {
            // 判断当前元素是否已被勾选
            const isChecked = watchTest.some(item => item.labelName === key && item.name === el.name);
            return (
              <Box key={`${key}-${el.name}`}>
                <FormControlLabel
                  sx={{ width: '20%', margin: '1em 2em' }}
                  control={<Checkbox checked={isChecked} />}
                  onChange={(event) => handleCheckboxChange({ event, key, el })}
                  label={el.name}
                />
              </Box>
            );
          })}
        </ul>
      </div>
    ))}
    {/* 已选元素的编辑区域 */}
    <Box mt={4}>
      <h2>已选择项编辑</h2>
      {fields.map((field, index) => {
        const filteredUsers = users?.filter(user => user.title.includes(field.name));
        return (
          <Box key={field.id} sx={{ display: 'flex', alignItems: 'center', margin: '1em 0' }}>
            <span>{`${field.labelName} - ${field.name}`}</span>
            <FormControl sx={{ width: '25%', margin: '0 2em' }}>
              {/* 正确的字段路径:test.索引.engineer */}
              <Select {...register(`test.${index}.engineer`)} defaultValue={field.engineer}>
                {filteredUsers?.map((user, userIndex) => (
                  <MenuItem key={userIndex} value={user.engineer}>
                    {user.engineer}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
            <Button onClick={() => remove(index)} variant="outlined" color="error">删除</Button>
          </Box>
        );
      })}
    </Box>
  </Box>
);

步骤3:修正表单字段路径

原代码中test${[index]}.engineer是错误的,React Hook Form中数组字段的正确路径格式是数组名.索引.字段名,也就是test.${index}.engineer。

这样修改后,选择工程师时就能正确更新test数组中对应元素的engineer字段了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 04:55:59