保持 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 步”做的,所以你有一个例子。
我使用 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 步”做的,所以你有一个例子。