Form initial default values on Edit.

I'm having a bit of trouble with my Refine Edit component. I'm trying to edit a record, and the Edit form opens, but the default values aren't being populated. The data is being fetched from the API correctly, but it's not reflecting in the form fields. I'm using useForm with defaultValues set, but it seems to be ignored. Any ideas on what I might be doing wrong? I'm using a custom dataProvider that expects query parameters (resource?id=eq.${id}) instead of params (resource/:id) for fetching data. But in resource defination and routes I have set routes with params format. can that be an issue? Here's a snippet of my code:
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
defaultValues: {
name: "",
description: "",
// ... other default values
},
refineCoreProps: {
redirect: false,
},
resolver: zodResolver(productFormSchema)
});
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
defaultValues: {
name: "",
description: "",
// ... other default values
},
refineCoreProps: {
redirect: false,
},
resolver: zodResolver(productFormSchema)
});
Passing this to Edit component from refinecore/mui
<Edit saveButtonProps={saveButtonProps}>
<Edit saveButtonProps={saveButtonProps}>
const data = useOne({ // resource: "products", // id: 1, // });
9 Replies
kapa.ai
kapa.ai4w ago
Hi @Mohit. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in your question and the knowledge sources available, it seems that there might be a few issues contributing to your problem. Let's address them one by one: 1. Default Values: The way you're setting default values might not be sufficient for an edit form. In an edit scenario, you typically want to populate the form with the existing data of the record being edited. 2. Custom Data Provider: Your custom data provider that uses query parameters instead of URL params could indeed be causing issues, especially if your resource definitions and routes are set up differently. 3. Data Fetching: It appears you're not using the useOne hook correctly to fetch the data for editing. Here's how you might approach solving these issues: 1. Use the defaultFormValues prop: Instead of setting static default values, you can use the defaultFormValues prop to fetch and set the initial values dynamically. Here's an example:
const { formProps, saveButtonProps, queryResult } = useForm<
Product,
HttpError,
Nullable<IProductFormData>
>({
refineCoreProps: {
resource: "products",
action: "edit",
id: id, // Make sure to pass the correct id here
},
defaultFormValues: async () => {
const response = await yourDataProvider.getOne({
resource: "products",
id: id,
});
return response.data;
},
resolver: zodResolver(productFormSchema)
});
const { formProps, saveButtonProps, queryResult } = useForm<
Product,
HttpError,
Nullable<IProductFormData>
>({
refineCoreProps: {
resource: "products",
action: "edit",
id: id, // Make sure to pass the correct id here
},
defaultFormValues: async () => {
const response = await yourDataProvider.getOne({
resource: "products",
id: id,
});
return response.data;
},
resolver: zodResolver(productFormSchema)
});
2. Ensure your data provider is correctly implemented: If your custom data provider uses a different URL structure, make sure it's correctly implemented and that Refine is aware of how to use it. You might need to override the getOne method in your data provider to use the correct URL structure. 3. Use the useForm hook correctly: The useForm hook from Refine should handle fetching the data for you when in edit mode. You don't need to use useOne separately if you're using useForm correctly. Here's a more complete example of how your Edit component might look:
import { useForm } from "@refinedev/react-hook-form";
import { Edit } from "@refinedev/mui";

const ProductEdit: React.FC = () => {
const {
refineCore: { formLoading, queryResult },
saveButtonProps,
register,
formState: { errors },
} = useForm<Product, HttpError, Nullable<IProductFormData>>({
refineCoreProps: {
resource: "products",
action: "edit",
id: id, // Make sure to pass the correct id here
},
resolver: zodResolver(productFormSchema)
});

return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<form>
<input {...register("name")} />
{errors.name && <span>{errors.name.message}</span>}

<input {...register("description")} />
{errors.description && <span>{errors.description.message}</span>}

{/* Add other form fields here */}
</form>
</Edit>
);
};
import { useForm } from "@refinedev/react-hook-form";
import { Edit } from "@refinedev/mui";

const ProductEdit: React.FC = () => {
const {
refineCore: { formLoading, queryResult },
saveButtonProps,
register,
formState: { errors },
} = useForm<Product, HttpError, Nullable<IProductFormData>>({
refineCoreProps: {
resource: "products",
action: "edit",
id: id, // Make sure to pass the correct id here
},
resolver: zodResolver(productFormSchema)
});

return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<form>
<input {...register("name")} />
{errors.name && <span>{errors.name.message}</span>}

<input {...register("description")} />
{errors.description && <span>{errors.description.message}</span>}

{/* Add other form fields here */}
</form>
</Edit>
);
};
This setup should correctly fetch the data for the product being edited and populate the form fields with the existing values. If you're still experiencing issues, it might be helpful to check the network requests in your browser's developer tools to ensure the correct data is being fetched from your API. For more detailed information on using useForm with Refine, you can refer to the Refine useForm documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@562507430341115905> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
adverse-sapphire
adverse-sapphireOP4w ago
Data provider resources and routes are correctly implemented. Here is my edit component :
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { ProductCreateForm } from "../../components";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError, useOne } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";

export const ProductEdit = () => {
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
// defaultValues: data?.data?.data[0] as Nullable<IProductFormData>,
refineCoreProps: {
redirect: false,
},
defaultValues: async () => {
//@ts-ignore
const response = await data_provider_typed.getOne({
resource: "products",
id: 146,
});
return response.data;
},

resolver: zodResolver(productFormSchema)
});

return (
<FormProvider {...methods}>
<DevTool control={methods.control} />
<Edit isLoading={isLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
/>

</Edit>
</FormProvider>
);
};
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { ProductCreateForm } from "../../components";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError, useOne } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";

export const ProductEdit = () => {
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
// defaultValues: data?.data?.data[0] as Nullable<IProductFormData>,
refineCoreProps: {
redirect: false,
},
defaultValues: async () => {
//@ts-ignore
const response = await data_provider_typed.getOne({
resource: "products",
id: 146,
});
return response.data;
},

resolver: zodResolver(productFormSchema)
});

return (
<FormProvider {...methods}>
<DevTool control={methods.control} />
<Edit isLoading={isLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
/>

</Edit>
</FormProvider>
);
};
Form elements are not getting the default values in here @kapa.ai Data provider resources and routes are correctly implemented. It is still not working with this code Here is my edit component :
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { ProductCreateForm } from "../../components";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError, useOne } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";
import { AutocompleteElement, TextFieldElement } from "react-hook-form-mui";


export const ProductEdit = () => {
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
// defaultValues: data?.data?.data[0] as Nullable<IProductFormData>,
refineCoreProps: {
redirect: false,
},
defaultValues: async () => {
//@ts-ignore
const response = await data_provider_typed.getOne({
resource: "products",
id: 146,
});
return response.data;
},

resolver: zodResolver(productFormSchema)
});

return (
<FormProvider {...methods}>
<DevTool control={methods.control} />
<Edit isLoading={isLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
/>

</Edit>
</FormProvider>
);
};
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { ProductCreateForm } from "../../components";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError, useOne } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";
import { AutocompleteElement, TextFieldElement } from "react-hook-form-mui";


export const ProductEdit = () => {
const methods = useForm<Product, HttpError, Nullable<IProductFormData>>({
// defaultValues: data?.data?.data[0] as Nullable<IProductFormData>,
refineCoreProps: {
redirect: false,
},
defaultValues: async () => {
//@ts-ignore
const response = await data_provider_typed.getOne({
resource: "products",
id: 146,
});
return response.data;
},

resolver: zodResolver(productFormSchema)
});

return (
<FormProvider {...methods}>
<DevTool control={methods.control} />
<Edit isLoading={isLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
/>

</Edit>
</FormProvider>
);
};
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the code you've provided and the information from the knowledge sources, there are a few adjustments we can make to help resolve the issue with populating default values in your edit form. Let's go through the changes: 1. First, you're using useForm from @refinedev/react-hook-form, which is correct. However, you're not utilizing all the properties it provides. Let's modify your code to make better use of it:
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { Edit } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";
import { TextFieldElement } from "react-hook-form-mui";

export const ProductEdit = () => {
const {
refineCore: { queryResult, formLoading },
saveButtonProps,
register,
control,
formState: { errors },
} = useForm<Product, HttpError, Nullable<IProductFormData>>({
refineCoreProps: {
resource: "products",
action: "edit",
id: "146", // Make sure to pass the correct id here
},
resolver: zodResolver(productFormSchema)
});

const productData = queryResult?.data?.data;
return (
<FormProvider {...methods}>
<DevTool control={control} />
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
control={control}
defaultValue={productData?.name}
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
control={control}
defaultValue={productData?.description}
/>
</Edit>
</FormProvider>
);
};
import { Nullable, Product, IProductFormData } from "@/interfaces";
import { Edit } from "@refinedev/mui";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { HttpError } from "@refinedev/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { productFormSchema } from "@/utils/zod/productFormValidation";
import { DevTool } from "@hookform/devtools";
import { TextFieldElement } from "react-hook-form-mui";

export const ProductEdit = () => {
const {
refineCore: { queryResult, formLoading },
saveButtonProps,
register,
control,
formState: { errors },
} = useForm<Product, HttpError, Nullable<IProductFormData>>({
refineCoreProps: {
resource: "products",
action: "edit",
id: "146", // Make sure to pass the correct id here
},
resolver: zodResolver(productFormSchema)
});

const productData = queryResult?.data?.data;
return (
<FormProvider {...methods}>
<DevTool control={control} />
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<TextFieldElement
name="name"
label={t("products.fields.name")}
fullWidth
autoFocus
control={control}
defaultValue={productData?.name}
/>
<TextFieldElement
name="description"
label={t("products.fields.description")}
multiline
rows={4}
fullWidth
control={control}
defaultValue={productData?.description}
/>
</Edit>
</FormProvider>
);
};
The key changes here are: 1. We're using the refineCore properties from useForm, which include queryResult and formLoading. 2. We're setting the resource, action, and id in the refineCoreProps. This tells Refine to fetch the data for the product with id 146. 3. We're passing the control prop to the TextFieldElement components, which is necessary for controlled inputs. 4. We're setting the defaultValue of each field to the corresponding value from productData. These changes should help populate your form with the existing data when editing a product. The useForm hook from Refine should handle fetching the data for you based on the refineCoreProps you provide. If you're still experiencing issues after making these changes, it might be helpful to check the network requests in your browser's developer tools to ensure the correct data is being fetched from your API. Also, make sure that your custom data provider is correctly implemented to handle the getOne request for fetching a single product. For more detailed information on using useForm with Refine, you can refer to the Refine useForm documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@562507430341115905> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
fair-rose
fair-rose4w ago
hello @Mohit I dont know the implementation of these components `import { AutocompleteElement, TextFieldElement } from "react-hook-form-mui";`` Do you see your form values when you debug like this:
const { watch } = useForm();
const formValues = watch();
console.log(formValues);
const { watch } = useForm();
const formValues = watch();
console.log(formValues);
adverse-sapphire
adverse-sapphireOP4w ago
Hello Alican, Thanks for responding. I am getting empty object while loggin form values like this at all time. Can there be any issue in react-hook-form-mui library? This library gives components to use MUI with RHF
fair-rose
fair-rose4w ago
Hello again @Mohit , I found the issue, async defaultValues doesn't work as expected when action: "edit" but it works when action: "create".
but it's kinda complicated task, because when action: "edit", useForm fetches data from the API and populates the form based on that data. Whichever returns later, overwrites the current data. There's another important point. The field fetched by useForm must be undefined for defaultFormValues to take effect.
What I mean is, if the data from the backend is an empty string (content: ""), it is still considered a value, so defaultFormValues won’t apply. you can set disable useForm fetching with queryOptions.enabled: false and you can use defaultValues
useForm<IPost, HttpError, Nullable<IPost>>({
defaultValues: async () => {
const data = await dataProvider(
"https://api.fake-rest.refine.dev",
).getOne({
resource: "posts",
id: 2,
});

return data.data as any;
},
refineCoreProps: { queryOptions: { enabled: false } },
})
useForm<IPost, HttpError, Nullable<IPost>>({
defaultValues: async () => {
const data = await dataProvider(
"https://api.fake-rest.refine.dev",
).getOne({
resource: "posts",
id: 2,
});

return data.data as any;
},
refineCoreProps: { queryOptions: { enabled: false } },
})
`
adverse-sapphire
adverse-sapphireOP3w ago
Got the issue, Thank you so much for helping out. Without your comment on this I would have never arrived to the solution. In my custom getOne function my backend is giving array of one object. For edit to work getOne must pass the object!! You are doing the good work 🙌

Did you find this page helpful?