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

React Hook Form中不使用setState更新Field Array并获取新旧值问题

React Hook Form Field Array:解决onBlur时无法获取字段新值的问题

问题根源分析

  1. 渲染时传递的data是组件初始化/重渲染时的快照,不会实时同步表单字段的最新值,所以data.qty始终显示初始值。
  2. CustomTextField中同时混用了useController和register,导致表单状态管理冲突。
  3. 旧值维护逻辑错误:event.target.defaultValue是字段的初始默认值,不是变更前的旧值,无法正确记录新旧值变化。

解决方案

1. 修复CustomTextField组件(核心修改)

移除冲突的register,仅用useController管理字段,同时正确维护新旧值的引用:

import { useController } from "react-hook-form";
import { useRef, useEffect } from "react";
import TextField from "@mui/material/TextField"; // 根据实际UI库调整

const CustomTextField = ({ name, onBlur }) => {
  const { field } = useController({ name });
  const oldValueRef = useRef(field.value);

  // 初始化/字段值更新时同步ref
  useEffect(() => {
    oldValueRef.current = field.value;
  }, [field.value]);

  const _handleChange = (event) => {
    // 先保存当前值为旧值,再更新新值
    oldValueRef.current = field.value;
    field.onChange(event);
  };

  const _handleBlur = (event) => {
    // 直接传递新旧值给父组件
    onBlur(event, {
      oldValue: oldValueRef.current,
      newValue: field.value
    });
  };

  return (
    <TextField
      {...field}
      onBlur={_handleBlur}
      onChange={_handleChange}
      value={field.value}
    />
  );
};

export default CustomTextField;

2. 父组件正确接收并使用新旧值

不再依赖渲染快照data,直接通过子组件传递的参数获取新旧值,或用getValues获取最新表单数据:

import { useForm, useFieldArray } from "react-hook-form";
import CustomTextField from "./CustomTextField";

const initialValues = {
  items: [{ qty: 2, cost: 10 }]
};

const App = () => {
  const { control, getValues } = useForm({
    defaultValues: initialValues
  });
  const { fields, append } = useFieldArray({
    control,
    name: "items"
  });

  const handleBlurQty = (event, { oldValue, newValue }, index) => {
    // 打印日志
    console.log(`第${index}个qty字段:旧值=${oldValue},新值=${newValue}`);
    // 如需获取完整的最新items数组
    const latestItems = getValues("items");
    console.log("最新items数组:", latestItems);
  };

  return (
    <div>
      {fields.map((item, index) => (
        <div key={item.id}>
          <CustomTextField
            name={`items.${index}.qty`}
            onBlur={(event, values) => handleBlurQty(event, values, index)}
          />
          <CustomTextField name={`items.${index}.cost`} />
        </div>
      ))}
      <button onClick={() => append({ qty: 0, cost: 0 })}>添加项目</button>
    </div>
  );
};

export default App;

关键说明

  • 避免状态冲突:React Hook Form中useController和register是两种不同的字段管理方式,不要在同一组件中混用。
  • 正确维护旧值:通过useRef存储变更前的字段值,在onChange触发前保存旧值,确保onBlur时能拿到准确的新旧值对比。
  • 实时获取最新数据:不要依赖渲染时的字段快照,通过getValues或子组件传递的参数获取表单最新状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:05:17