So i have a form that has custom fields that i add via Field Array from react-hook-form. And everything works but i added drag and drop for the property items (to reorder them) and now it would be a big mess to show all these many fields directly so i moved them in a Dialog.
Here are the pictures to get the idea what is easier to drag n drop... (the right one)
The problem is that field array values get "reset" after the modal closes (after i type those form values in edit modal), i guess it has something to do with re-rendering but i am not sure.
I tried to show the minimal code example here without d&d and other useless stuff...
But here is codesandbox playground with the full code
CreateCategoryForm.js
const defaultValues = {
name: "",
slug: "",
description: "",
properties: [] // field array
}
function CreateCategoryForm() {
const methods = useForm({ defaultValues });
const { handleSubmit, control, errors } = methods;
const { fields, append, remove, swap } = useFieldArray({ name: "properties", control });
const onSubmit = async (data) => {
console.log("data: ", data);
};
return (
<Container>
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<FormTextField name="name" />
<FormTextField name="slug" />
<FormTextField name="description" />
{fields.map((card, idx) => (
<PropertyCard key={card.id} card={card} idx={idx} errors={errors} remove={remove} />
))}
<Button onClick={() => append({ name: "", label: "", type: "text", filterable: true })}>
Add Property
</Button>
<FormSubmitButton>
Create Category
</FormSubmitButton>
</form>
</FormProvider>
</Container>
);
}
PropertyCard.js
function PropertyCard({ card, errors, idx, remove }) {
const [dialogOpen, setDialogOpen] = React.useState(false);
const handleOpenDialog = () => {
setDialogOpen(true);
};
const handleCloseDialog = () => {
setDialogOpen(false);
};
return (
<div>
Property {idx + 1}
<IconButton onClick={() => handleOpenDialog()}>
edit
</IconButton>
<IconButton onClick={() => remove(idx)}>
X
</IconButton>
<Dialog
fullScreen
open={dialogOpen}
onClose={handleCloseDialog}
>
<Container maxWidth="xs">
<FormTextField
name={`properties[${idx}].name`}
label="Property Name"
/>
<FormTextField
name={`properties[${idx}].label`}
label="Property Label"
/>
<FormSelect
name={`properties[${idx}].type`}
label="Filter Type"
options={[
{ label: "text", value: "text" },
{ label: "bool", value: "bool" }
]}
defaultValue="text"
/>
<FormSwitch
name={`properties[${idx}].filterable`}
label="Filterable"
defaultValue={true}
/>
<IconButton onClick={handleCloseDialog}>
X
</IconButton>
</Container>
</Dialog>
</div>
);
}
As @Bill mentioned in the comment, shouldUnregister: false
seemed to do the trick.
So i changed useForm to this:
const methods = useForm({ defaultValues, shouldUnregister: false });
And for each property input i added defautlValue to make it work.
<FormTextField
name={`properties[${idx}].name`}
label="Property Name"
defaultValue={getValues()?.properties?.[idx]?.name} // added this
/>
<FormTextField
name={`properties[${idx}].label`}
label="Property Label"
defaultValue={getValues()?.properties?.[idx]?.label} // added this
/>
<FormSelect
name={`properties[${idx}].type`}
label="Filter Type"
options={[
{ label: "text", value: "text" },
{ label: "bool", value: "bool" }
]}
defaultValue={getValues()?.properties?.[idx]?.type || 'text'} // added this
/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With