React Hook Form中不使用setState更新Field Array并获取新旧值问题
React Hook Form Field Array:解决onBlur时无法获取字段新值的问题
问题根源分析
- 渲染时传递的
data是组件初始化/重渲染时的快照,不会实时同步表单字段的最新值,所以data.qty始终显示初始值。 CustomTextField中同时混用了useController和register,导致表单状态管理冲突。- 旧值维护逻辑错误:
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
相关产品推荐
相关产品推荐

