Final-form重新初始化时如何保留脏值?(类redux-form的keepDirtyOnReinitialize)
在Final-form中重新初始化时保留脏值的解决方案
确实,Final-form并没有像redux-form那样内置keepDirtyOnReinitialize这类直接配置项,但我们可以通过它提供的灵活API手动实现这个功能,完美适配你这种大型表单、子表单编辑不丢失父表单修改的场景。下面是两种实用的实现方式:
方法一:手动合并脏值后初始化
这是最直接的方式,当需要重新初始化表单时,先获取当前表单的脏字段和对应值,再合并到新的初始化数据中,最后调用initialize方法。
举个React+Final-form的例子:
import { useForm, useEffect } from 'react-final-form'; const LargeParentForm = ({ initialParentData }) => { const { form, handleSubmit, values } = useForm({ initialValues: initialParentData, onSubmit: (submittedValues) => { // 提交逻辑 }, }); // 监听父表单初始化数据的变化(比如子表单编辑后触发更新) useEffect(() => { if (!initialParentData) return; // 获取当前表单状态中的脏字段 const { dirtyFields } = form.getState(); // 合并新的初始化数据和脏值:只保留用户修改过的字段 const mergedInitials = { ...initialParentData, ...Object.fromEntries( Object.entries(values).filter(([key]) => dirtyFields[key]) ), }; // 用合并后的数据初始化表单 form.initialize(mergedInitials); }, [initialParentData, form, values]); // 表单渲染逻辑... return ( <form onSubmit={handleSubmit}> {/* 父表单字段 */} <Field name="parentField1" component="input" type="text" /> <Field name="parentField2" component="input" type="text" /> {/* 子表单组件 */} <ChildForm onChildDataChange={(newChildData) => { // 这里触发父表单initialParentData更新,进而触发上面的useEffect重新初始化 }} /> </form> ); };
如果你的表单包含嵌套字段,需要递归处理合并逻辑,避免丢失嵌套层级的脏值:
// 递归合并嵌套的脏值 const mergeNestedDirtyValues = (newInitials, currentValues, dirtyFields) => { return Object.entries(newInitials).reduce((merged, [key, val]) => { if (typeof val === 'object' && val !== null && dirtyFields[key]) { // 嵌套对象递归合并 merged[key] = mergeNestedDirtyValues(val, currentValues[key], dirtyFields[key]); } else if (dirtyFields[key]) { // 普通字段直接取当前脏值 merged[key] = currentValues[key]; } else { // 非脏字段用新的初始化值 merged[key] = val; } return merged; }, {}); };
把之前的mergedInitials替换成这个函数的返回值即可。
方法二:封装自定义Mutator复用逻辑
如果多个表单都需要这个功能,建议封装成Final-form的Mutator,方便全局复用:
import { useForm } from 'react-final-form'; // 定义自定义Mutator:初始化时保留脏值 const initializeKeepingDirty = (form) => (newInitialValues) => { const { dirtyFields, values } = form.getState(); // 这里可以用上面的递归合并函数处理嵌套字段 const mergedValues = { ...newInitialValues, ...Object.fromEntries( Object.entries(values).filter(([key]) => dirtyFields[key]) ), }; form.initialize(mergedValues); }; // 在表单中使用这个Mutator const LargeParentForm = ({ initialParentData }) => { const { form, handleSubmit } = useForm({ initialValues: initialParentData, onSubmit: (submittedValues) => {}, // 注册自定义Mutator mutators: { initializeKeepingDirty, }, }); // 当需要重新初始化时,直接调用自定义Mutator const handleReinitialize = (newData) => { form.mutators.initializeKeepingDirty(newData); }; return ( <form onSubmit={handleSubmit}> {/* 表单内容 */} <ChildForm onChildDataChange={(newChildData) => { // 调用自定义方法重新初始化 handleReinitialize({ ...initialParentData, childData: newChildData }); }} /> </form> ); };
注意事项
- 如果你使用了
final-form-arrays处理数组字段,需要额外处理数组的脏值逻辑,确保数组中被修改的项不会被重置。 - 这种方式本质上是在初始化时用用户已修改的值覆盖新的初始值,所以要确保合并逻辑正确,避免意外覆盖非脏字段。
内容的提问来源于stack exchange,提问作者Ivan Buryak
相关产品推荐
相关产品推荐

