保持 useFieldArray 的状态 react-hook-form

Maintain state of useFieldArray react-hook-form

我使用 react-hook-form 构建了一个多步骤表单,并使用 useFieldArray 构建了一个动态字段数组。

文档:useFieldArray documentation

这是完整的工作代码link:React Multi-step form with useFieldArray

在第 2 步中,当我使用 添加狗 按钮添加新字段时,一切正常,步骤的新数据使用 小状态机.

但是当我点击上一个按钮时,添加的字段消失了,而数据仍在本地存储中。

第二步代码:

import { useForm, useFieldArray } from "react-hook-form";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { useNavigate } from "react-router-dom";

function Step2(props) {
const {
    register,
    control,
    handleSubmit,
    watch,
    formState: { errors },
} = useForm({
    defaultValues: {
        test: [{ nameOfDog: "Bill", ageOfDog: "2", sizeOfDog: "small" }],
    },
});

const { fields, append, remove } = useFieldArray({
    control,
    shouldUnregister: true,
    name: "test",
});

const elements = watch("test");
console.log(elements, fields);

const { actions, state } = useStateMachine({ updateAction });
const navigate = useNavigate();
const onSubmit = (data) => {
    // console.log(fields);
    actions.updateAction(data);
    navigate("/step3");
};

let dta;
if (state.date2) {
    dta = new Date(state.date2);
} else {
    dta = new Date();
    dta.setDate(dta.getDate() + 1);
}

return (
    <form className="form" onSubmit={handleSubmit(onSubmit)}>
        <div className="stepn stepn-active" data-step="1">
            {fields.map((item, index) => {
                return (
                    <div className="row" key={item.id}>
                        <div className="col">
                            <label htmlFor="nameOfDog">Name:</label>
                            <input
                                id="nameOfDog"
                                {...register(`test.${index}.nameOfDog`, {
                                    required: true,
                                })}
                                defaultValue={item.nameOfDog}
                            />
                            {errors.nameOfDog && (
                                <span>This field is required</span>
                            )}
                        </div>
                        <div className="col">
                            <label htmlFor="ageOfDog">Age:</label>
                            <input
                                id="ageOfDog"
                                type="number"
                                {...register(`test.${index}.ageOfDog`, {
                                    required: true,
                                })}
                                defaultValue={item.ageOfDog}
                            />
                            {errors.ageOfDog && (
                                <span>This field is required</span>
                            )}
                        </div>
                        <div className="col">
                            <label htmlFor="sizeOfDog">Size in Lbs:</label>
                            <select
                                id="sizeOfDog"
                                {...register(`test.${index}.sizeOfDog`, {
                                    required: true,
                                })}
                                defaultValue={item.sizeOfDog || ""}
                            >
                                <option value="small">Small (40)</option>
                                <option value="large">Large (40+)</option>
                            </select>
                            {errors.sizeOfDog && (
                                <span>Please Select an option</span>
                            )}
                        </div>
                        <div className="col">
                            <button
                                onClick={(e) => {
                                    e.preventDefault();
                                    remove(index);
                                }}
                                style={{ padding: "26px 62px" }}
                            >
                                Delete
                            </button>
                        </div>
                    </div>
                );
            })}
            <div className="row">
                <div className="col">
                    <button
                        onClick={(e) => {
                            e.preventDefault();
                            append({
                                nameOfDog: "Bill2",
                                ageOfDog: "5",
                                sizeOfDog: "large",
                            });
                        }}
                    >
                        Add a Dog
                    </button>
                </div>
            </div>
        </div>

        {/* <input type="submit" /> */}
        <div className="row">
            <button className="prev" onClick={() => navigate("/")}>
                Previous
            </button>
            <button className="next">Next</button>
        </div>
    </form>
    );
   }

 export default Step2;

{fields.map((item, index) =>

只要单击上一个按钮,字段 数组就会重置为默认值。

当我们返回上一步时,将保存除第 2 步之外的所有剩余表单步骤。

如何在单击上一个按钮时保存第 2 步中的字段。

它很长,但也许我们可以弄明白。

使用正确,我认为问题是你没有检查状态,只是每次都打印默认值

这里有两个问题:

  • 当您单击“上一步”按钮时,您不会在“第 2 步”中更新您的状态。所以你必须将当前的表单数据传递给你的状态机。此外,当您想进行上一步时,您现在也没有对“第 2 步”进行表单验证。要添加对验证的支持,您应该将 handleSubmit<form /> 元素移走,并将其传递给您的两个 <button /> 元素。这样你就可以摆脱 watch 调用,因为你在 handleSubmit 回调中有当前的表单数据。
const onPrevious = (data) => {
  actions.updateAction(data);

  navigate("/");
};
const onNext = (data) => {
  actions.updateAction(data);

  navigate("/step3");
};
<div className="row">
  <button className="prev" onClick={handleSubmit(onPrevious)}>
    Previous
  </button>
  <button className="next" onClick={handleSubmit(onNext)}>
    Next
  </button>
</div>

如果您想在 <form /> 元素中保留 handleSubmit,您应该使用 watch 并将数据传递到您的状态机,然后再导航回上一步。

const test = watch("test");

const onPrevious = (data) => {
  actions.updateAction({ test });

  navigate("/");
};
  • 当您在步骤更改中重新初始化每个步骤组件时,您必须将每个步骤的当前 defaultValues 传递给 useForm。对于“第 2 步”,它看起来像这样:
const {
  register,
  control,
  handleSubmit,
  watch,
  formState: { errors }
} = useForm({
  defaultValues: {
    test: state.test ?? [
      { nameOfDog: "Bill", ageOfDog: "2", sizeOfDog: "small" }
    ]
  }
});

要更改的重要一点是,当您在 useForm 配置中为您的字段传递 defaultValues 时,您应该将其从 <Controller /> 组件中删除。我是为“第 1 步”做的,所以你有一个例子。