stormy-gold
stormy-gold3w ago

React-hook-form for antd

so in this link https://refine.dev/docs/packages/react-hook-form/introduction/ it describes react-hook-form implementation for headless/materialui/chakraui. how about antd?
Introduction | Refine
Refine provides an integration package for React Hook Form library. This package enables you to manage your forms in a headless manner. This adapter supports all of the features of both React Hook Form and Refine's useForm hook. Simply, you can use any of the React Hook Form examples as-is by copying and pasting them into your project.
32 Replies
kapa.ai
kapa.ai3w ago
Hi @biohazard#1521. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- For Ant Design, Refine provides its own specialized useForm hook directly from the @refinedev/antd package rather than using the React Hook Form integration. This is because Ant Design has its own form handling system that Refine integrates with natively. The @refinedev/antd package includes a useForm hook that is specifically designed to work with Ant Design's <Form /> component. This hook extends Refine's core useForm hook with additional features tailored for Ant Design forms. Here's a basic example of how to use the Ant Design form integration:
import { useForm, Create } from "@refinedev/antd";
import { Form, Input } from "antd";

const CreatePage = () => {
const { formProps, saveButtonProps } = useForm();

return (
<Create saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Name" name="name">
<Input />
</Form.Item>
<Form.Item label="Material" name="material">
<Input />
</Form.Item>
</Form>
</Create>
);
};
import { useForm, Create } from "@refinedev/antd";
import { Form, Input } from "antd";

const CreatePage = () => {
const { formProps, saveButtonProps } = useForm();

return (
<Create saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Name" name="name">
<Input />
</Form.Item>
<Form.Item label="Material" name="material">
<Input />
</Form.Item>
</Form>
</Create>
);
};
You can find more detailed information about using forms with Ant Design in Refine in the following resources: - Ant Design Form Integration Documentation - Form Examples with Ant Design Refine also provides additional form-related hooks for Ant Design such as: - useDrawerForm for forms in drawers - useModalForm for forms in modals - useStepsForm for multi-step forms These specialized hooks make it easier to implement different types of form patterns in your Refine applications with Ant Design. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai the problem with useForm from antd, i can't find a way to validate the form using zod schema for example
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources provided, there isn't specific information about using Zod schema validation with Refine's Ant Design useForm hook. The documentation primarily shows validation using Ant Design's built-in validation system through the rules property of Form.Item. For custom validations with Ant Design forms in Refine, the documentation mentions using the validator function within the Form.Item rules property:
<Form.Item
label="Name"
name="name"
rules={[
{
required: true,
validator: async (_, value) => {
// Custom validation logic here
if (!value) {
return Promise.reject('Please enter a name');
}
}
}
]}
>
<Input />
</Form.Item>
<Form.Item
label="Name"
name="name"
rules={[
{
required: true,
validator: async (_, value) => {
// Custom validation logic here
if (!value) {
return Promise.reject('Please enter a name');
}
}
}
]}
>
<Input />
</Form.Item>
If you're looking for schema-based validation similar to Zod, you might want to check out the Custom Form Validation example which demonstrates more advanced validation techniques with Ant Design forms. For React Hook Form with Zod integration, Refine does provide support through @refinedev/react-hook-form, but as we discussed earlier, this is primarily for headless, Material UI, and Chakra UI implementations rather than Ant Design. If you need schema validation with Ant Design forms, you might need to implement a custom solution that combines Ant Design's form validation system with Zod schema validation in your form submission logic. Would you like me to explore any specific aspect of form validation with Refine's Ant Design integration in more detail? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai the problem is that i don't want to use the antd validation because it is ugly and needs to be implemented per field. i want to work with zod/yup schema
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I understand your preference for using schema-based validation with Zod or Yup instead of Ant Design's field-by-field validation approach. Based on the knowledge sources, there is a way to use schema validation with Refine forms, but the examples primarily show this with React Hook Form rather than directly with Ant Design's form system. For schema validation with Yup, there's an example in the React Hook Form integration:
import * as Yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "@refinedev/react-hook-form";
import { HttpError } from "@refinedev/core";

const schema = Yup.object().shape({
firstname: Yup.string().label("First Name").trim().required().min(3).max(64),
lastname: Yup.string().label("Last Name").trim().required().min(3).max(64),
// other validations...
});

const { control } = useForm<IFormValue, HttpError, IFormValue>({
mode: "onChange",
defaultValues,
resolver: yupResolver(schema),
});
import * as Yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "@refinedev/react-hook-form";
import { HttpError } from "@refinedev/core";

const schema = Yup.object().shape({
firstname: Yup.string().label("First Name").trim().required().min(3).max(64),
lastname: Yup.string().label("Last Name").trim().required().min(3).max(64),
// other validations...
});

const { control } = useForm<IFormValue, HttpError, IFormValue>({
mode: "onChange",
defaultValues,
resolver: yupResolver(schema),
});
For Zod validation, there's a Discord thread that mentions using Zod with useStepsForm:
const form = useStepsForm<BaseRecord, HttpError, CreatePropertyFields>({
resolver: zodResolver(createFormScheme),
stepsProps: {
isBackValidate: false
},
defaultValues: {
// your default values
}
});
const form = useStepsForm<BaseRecord, HttpError, CreatePropertyFields>({
resolver: zodResolver(createFormScheme),
stepsProps: {
isBackValidate: false
},
defaultValues: {
// your default values
}
});
However, there appears to be a bug with useStepsForm and Zod validation as mentioned in the Discord thread, where it tries to validate fields that haven't been shown yet in multi-step forms. For server-side validation, Refine's Ant Design integration does support handling validation errors returned from the server. When the dataProvider returns a rejected promise with an errors field, the form will automatically update with those errors. If you're determined to use schema validation with Ant Design forms, you might need to: 1. Use the React Hook Form integration with Refine and manually connect it to Ant Design components 2. Create a custom implementation that uses Zod/Yup for validation but still works with Ant Design's form components Unfortunately, the knowledge sources don't provide a direct example of using Zod/Yup schema validation with Refine's Ant Design form integration. Would you like me to explore any specific aspect of these approaches in more detail? React Hook Form Validation with Complete Examples provides more information about schema validation with Yup. Discord support thread shows an example of using Zod with useStepsForm. Server-Side Form Validation demonstrates handling validation errors from the server. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai ok so i tried the headless way import { HttpError } from '@refinedev/core'; import { useForm } from '@refinedev/react-hook-form'; import { Input } from 'antd'; export const ProposalEdit = () => { const { refineCore: { onFinish, formLoading, queryResult }, register, handleSubmit, formState: { errors, ...rest }, } = useForm<IPost, HttpError>({ refineCoreProps: { resource: 'proposal', action: 'edit', // id: 1, }, }); console.log('REST - ', register('name', { required: true }), queryResult); return ( <form onSubmit={handleSubmit(onFinish)}> <label>Name: </label> <Input {...register('name', { required: true })} /> <input {...register('name', { required: true })} /> {errors.title && <span>This field is required</span>} <br /> <label>Status: </label> <select {...register('status')}> <option value="published">published</option> <option value="draft">draft</option> <option value="rejected">rejected</option> </select> <br /> <label>Content: </label> <textarea {...register('content', { required: true })} rows={10} cols={50} /> {errors.content && <span>This field is required</span>} <br /> <input type="submit" value="Submit" /> {formLoading && <p>Loading</p>} </form> ); }; export type IStatus = 'published' | 'draft' | 'rejected'; interface IPost { id: number; title: string; content: string; status: IStatus; } but the name field doesn't get filled even though the query should have it it exists in queryResult.data.name
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I see the issue with your form not populating the default values from the query result. Based on the knowledge sources, there are a few ways to handle this when using @refinedev/react-hook-form. The problem is that even though your data is being fetched correctly (as you can see in queryResult.data.name), it's not automatically being set as the default value for your form fields. Here are a few approaches to fix this: 1. Use the defaultValues function You can use an async defaultValues function to fetch and set the initial values:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1,
},
defaultValues: async () => {
// You can use your data provider directly here
const response = await dataProvider.getOne({
resource: "proposal",
id: id, // Make sure you have the ID
});
return response.data;
},
});
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1,
},
defaultValues: async () => {
// You can use your data provider directly here
const response = await dataProvider.getOne({
resource: "proposal",
id: id, // Make sure you have the ID
});
return response.data;
},
});
2. Use React's useEffect to update form values Another approach is to use useEffect to set the form values when the query data is loaded:
import { useEffect } from 'react';

// Add this to your component
const { setValue } = useForm(); // Make sure to destructure setValue

useEffect(() => {
if (queryResult?.data?.data) {
const record = queryResult.data.data;
setValue("name", record.name);
setValue("status", record.status);
setValue("content", record.content);
}
}, [queryResult?.data?.data, setValue]);
import { useEffect } from 'react';

// Add this to your component
const { setValue } = useForm(); // Make sure to destructure setValue

useEffect(() => {
if (queryResult?.data?.data) {
const record = queryResult.data.data;
setValue("name", record.name);
setValue("status", record.status);
setValue("content", record.content);
}
}, [queryResult?.data?.data, setValue]);
3. Use FormProvider with defaultValues As shown in one of the Discord support threads:
const methods = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1,
},
defaultValues: queryResult?.data?.data, // Set this directly if data is already loaded
});

// Then in your JSX
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onFinish)}>
{/* Your form fields */}
</form>
</FormProvider>
);
const methods = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1,
},
defaultValues: queryResult?.data?.data, // Set this directly if data is already loaded
});

// Then in your JSX
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onFinish)}>
{/* Your form fields */}
</form>
</FormProvider>
);
The issue might also be related to the timing of when the data is loaded versus when the form is initialized. Make sure you're handling the loading state properly and only rendering the form when data is available. Also, check if you're providing the correct ID for the record you want to edit. In your code, you've commented out the id property in refineCoreProps, which might be why the correct record isn't being fetched. React Hook Form Introduction provides more information about using React Hook Form with Refine. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai what is this nonsense, i am sure that the query fetches correctly, so the hook form doesn't have any sort of integration for initialization of an edit form? it should be basic
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I understand your frustration. You're right that initializing form values for an edit form should be straightforward. Based on the knowledge sources, there is indeed built-in functionality in useForm from @refinedev/react-hook-form to handle this, but there might be some issues with how it's working in your case. According to the documentation, when using useForm with action: "edit", it should automatically fetch the record data and populate the form fields. Let me explain what might be happening: 1. From the Discord support thread, there was a similar issue where default values weren't being populated. The support staff identified that:
"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."
2. Another issue could be related to how your data provider is returning data. One user in the Discord thread found that their backend was returning "array of one object" instead of a single object, which was causing issues:
"In my custom getOne function my backend is giving array of one object. For edit to work getOne must pass the object!!"
3. There's also a potential issue with the timing of when data is loaded versus when the form is initialized. Here's what you can try:
const {
refineCore: { onFinish, formLoading, query },
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: id, // Make sure this is set correctly
},
});

// Use useEffect to reset the form with the fetched data
useEffect(() => {
if (query?.data?.data) {
reset(query.data.data);
}
}, [query?.data?.data, reset]);
const {
refineCore: { onFinish, formLoading, query },
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: id, // Make sure this is set correctly
},
});

// Use useEffect to reset the form with the fetched data
useEffect(() => {
if (query?.data?.data) {
reset(query.data.data);
}
}, [query?.data?.data, reset]);
This approach uses the reset function from React Hook Form to set all form values once the data is loaded. If you're still having issues, you might want to check: 1. The exact structure of your queryResult.data 2. Whether your field names in the form match exactly with the property names in your data 3. If there are any transformations happening in your data provider React Hook Form Usage documentation shows that the edit functionality should work automatically when properly configured. I hope this helps resolve your issue! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai so my getOne method just returns data object. basically in queryResult i see data object which holds all the relevant values, including name. why wouldn't it initialize then?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, there are several reasons why your form fields might not be initializing with the data from your getOne method, even though you can see the data in queryResult. From the GitHub issues and Discord support threads, there appears to be a known issue with form initialization in edit mode. Here are the most likely causes: 1. Race condition between data fetching and form initialization: According to a GitHub issue: "When action is 'edit' or 'clone' a race condition with async defaultFormValues may occur. In this case, the form values will be the result of the last completed operation." Default Form Values Documentation 2. Form initialization timing: The form might be initializing before the data is fetched. As mentioned in a GitHub issue: "We've set the default values of the form fields by adding the defaultValues config to useForm()'s configuration argument object" React Hook Form Blog 3. Integration between useForm and data fetching: From a GitHub issue about this exact problem: "When using { queryResult } = useShow() and passing queryResult to use(Modal)Form with defaultFormValues, often yields empty defaultFormValues." GitHub Issue #1263 To fix this issue, you could try: 1. Use the setId function that comes from useForm:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
formState: { errors },
setId
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
},
});
// Make sure to set the ID after the form is initialized
useEffect(() => {
setId(id); // Your record ID
}, [setId, id]);
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
formState: { errors },
setId
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
},
});
// Make sure to set the ID after the form is initialized
useEffect(() => {
setId(id); // Your record ID
}, [setId, id]);
This approach was suggested in a GitHub issue as a solution: "In fact, all you have to do is call the 'setId' function, which returns from the useForm hook, with the id of the record you want to edit." GitHub Issue #1729 2. Alternatively, you could disable the automatic fetching and set the values manually:
const {
refineCore: { onFinish, formLoading },
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
queryOptions: { enabled: false } // Disable automatic fetching
},
defaultValues: async () => {
// Fetch data manually
const response = await dataProvider.getOne({
resource: "proposal",
id: id,
});
return response.data;
},
});
const {
refineCore: { onFinish, formLoading },
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
queryOptions: { enabled: false } // Disable automatic fetching
},
defaultValues: async () => {
// Fetch data manually
const response = await dataProvider.getOne({
resource: "proposal",
id: id,
});
return response.data;
},
});
This approach was suggested in a Discord support thread: "you can set disable useForm fetching with queryOptions.enabled: false and you can use defaultValues" The issue seems to be a known limitation with how the form initialization works with edit mode in Refine, particularly with the timing of when data is fetched versus when the form is initialized. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai i even added logs const { refineCore: { onFinish, formLoading, queryResult }, register, handleSubmit, formState: { errors, ...rest }, } = useForm<IPost, HttpError>({ refineCoreProps: { resource: 'proposal', action: 'edit', // id: 1, queryOptions: { onSuccess: (data) => { console.log('DATA - ', data); }, onError: (error) => { console.log('ERROR - ', error); }, }, }, }); and i see the data log properly with just the object in question. how can we log the form values to try and monitor if anything happens on initialization?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, you can monitor form values in React Hook Form using the watch() method. This will help you see what's happening with your form values during initialization and as they change. Here's how you can implement it:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
queryOptions: {
onSuccess: (data) => {
console.log('DATA - ', data);
},
onError: (error) => {
console.log('ERROR - ', error);
},
},
},
});

// Log all form values
const formValues = watch();
console.log('Current form values:', formValues);

// Or watch specific fields
const nameField = watch("name");
console.log('Name field value:', nameField);
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
watch,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
queryOptions: {
onSuccess: (data) => {
console.log('DATA - ', data);
},
onError: (error) => {
console.log('ERROR - ', error);
},
},
},
});

// Log all form values
const formValues = watch();
console.log('Current form values:', formValues);

// Or watch specific fields
const nameField = watch("name");
console.log('Name field value:', nameField);
As mentioned in the React Hook Form documentation:
"We can subscribe to form data using the watch() API. The entire form data object can be watched. Or individual fields of interest can also be watched. Watching form fields with the watch() API triggers re-renders, as it happens with pure controlled fields." Watching Form Fields in React Hook Form with watch()
This will help you monitor what's happening with your form values and see if they're being properly initialized from your query data. You can also add a useEffect to see when the form values change:
useEffect(() => {
console.log('Form values changed:', watch());
}, [watch()]);
useEffect(() => {
console.log('Form values changed:', watch());
}, [watch()]);
This should help you debug what's happening with your form initialization and see if the values from your query are being properly set in the form. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai i get name undefined constantly, so i presume no initialization happens. what are we missing here? it should work out of the box so what can be wrong?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources and our conversation, there are several potential issues that could be preventing the form initialization in your edit form. Let's explore what might be wrong: 1. Missing ID parameter: I notice in your code that you've commented out the id parameter in refineCoreProps. This is crucial for edit forms:
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1, // This is commented out
}

refineCoreProps: {
resource: 'proposal',
action: 'edit',
// id: 1, // This is commented out
}

According to GitHub Issue #1729, you need to either: - Provide the id directly in the refineCoreProps - Use the setId function returned from useForm 2. Data structure mismatch: The data returned by your getOne method might not be in the structure that Refine expects. Check if your data is nested under a data property (like queryResult.data.data.name instead of queryResult.data.name). 3. Form field name mismatch: Ensure that the field names in your form match exactly with the property names in your data object. 4. Race condition: There's a known race condition issue with useForm in edit mode, as mentioned in GitHub Issue #4422. The form might be initializing before the data is fetched. Here's what you can try:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
setValue,
watch,
setId,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// Explicitly set the ID here if you have it
id: yourRecordId,
},
});

// Log the current form values
console.log('Form values:', watch());

// Log the query result
console.log('Query result:', queryResult?.data);
// Use useEffect to manually set form values when data is loaded
useEffect(() => {
if (queryResult?.data?.data) {
console.log('Setting form values from query data:', queryResult.data.data);
// Assuming your data structure is queryResult.data.data
setValue("name", queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
setValue,
watch,
setId,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
// Explicitly set the ID here if you have it
id: yourRecordId,
},
});

// Log the current form values
console.log('Form values:', watch());

// Log the query result
console.log('Query result:', queryResult?.data);
// Use useEffect to manually set form values when data is loaded
useEffect(() => {
if (queryResult?.data?.data) {
console.log('Setting form values from query data:', queryResult.data.data);
// Assuming your data structure is queryResult.data.data
setValue("name", queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
Another approach from the Discord support thread suggests disabling the automatic query and using async defaultValues:
useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: yourRecordId,
queryOptions: { enabled: false }
},
defaultValues: async () => {
const response = await dataProvider.getOne({
resource: "proposal",
id: yourRecordId,
});
return response.data;
},
})
useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: yourRecordId,
queryOptions: { enabled: false }
},
defaultValues: async () => {
const response = await dataProvider.getOne({
resource: "proposal",
id: yourRecordId,
});
return response.data;
},
})
The issue might also be related to how your data provider is structured. Make sure it returns data in the format that Refine expects for the getOne endpoint. Let me know if any of these approaches help or if you need further assistance! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai tried setting id. queryResult.data.name is already available. name field should be a matching then or should it be queryResult.data.data.name?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, the structure of the data returned by useForm in edit mode depends on how your data provider is structured. Let's clarify the correct data path: When using useForm from @refinedev/react-hook-form in edit mode, the query result typically has this structure:
queryResult = {
data: {
data: {
name: "Your name value",
// other fields
}
}
}
queryResult = {
data: {
data: {
name: "Your name value",
// other fields
}
}
}
So the correct path to access your field would be queryResult.data.data.name, not just queryResult.data.name. This is because Refine's data providers typically wrap the actual record in a data property inside the response object. This is mentioned in several examples in the documentation, such as:
const { query } = useForm();
const record = query.data?.data; // Note the double data access
const { query } = useForm();
const record = query.data?.data; // Note the double data access
Forms Basic Usage shows this pattern in their examples. To fix your issue, try accessing the data with:
useEffect(() => {
if (queryResult?.data?.data) {
console.log('Setting form values from query data:', queryResult.data.data);
setValue("name", queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
useEffect(() => {
if (queryResult?.data?.data) {
console.log('Setting form values from query data:', queryResult.data.data);
setValue("name", queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
Or if you're logging the data:
console.log('Name value:', queryResult?.data?.data?.name);
console.log('Name value:', queryResult?.data?.data?.name);
If your data structure is different, you might need to adjust your data provider to match Refine's expected format, or modify how you access the data in your component. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai so once i switched to queryResult.data.data, it started working wiuth this approach: import { HttpError } from '@refinedev/core'; import { useForm } from '@refinedev/react-hook-form'; import { Input } from 'antd'; import { useEffect } from 'react'; export const ProposalEdit = () => { const { refineCore: { onFinish, formLoading, queryResult }, register, handleSubmit, setValue, watch, setId, formState: { errors }, } = useForm<IPost, HttpError>({ refineCoreProps: { resource: 'proposal', action: 'edit', // Explicitly set the ID here if you have it // id: 'ad14317b-a8af-444c-8e7c-817d2c830659', }, }); // Log the current form values console.log('Form values:', watch()); // Log the query result console.log('Query result:', queryResult?.data); // Use useEffect to manually set form values when data is loaded useEffect(() => { if (queryResult?.data?.data) { console.log( 'Setting form values from query data:', queryResult.data.data, ); // Assuming your data structure is queryResult.data.data setValue('name', queryResult.data.data.name); // Set other fields as needed } }, [queryResult?.data?.data, setValue]); return ( <form onSubmit={handleSubmit(onFinish)}> <label>Name: </label> <Input {...register('name', { required: true })} /> <input {...register('name', { required: true })} /> {errors.title && <span>This field is required</span>} <br /> however the field was only prefilled on the htmlinput, but not antd input. why? also we still needed the useEffect hook
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I can see you've made progress by accessing the correct data path (queryResult.data.data), but you're still facing two issues: 1. The Ant Design <Input /> component isn't being populated while the HTML <input /> is 2. You still need to use the useEffect hook to manually set values Why Ant Design Input Isn't Being Populated The issue with Ant Design's <Input /> component not being populated is related to how React Hook Form integrates with Ant Design components. When using register() with Ant Design components, there's a compatibility issue because Ant Design components expect props in a different format than what register() provides. For Ant Design components, you need to use the Controller component from React Hook Form:
import { Controller } from "react-hook-form";

// In your form
<Controller
name="name"
control={control} // Make sure to destructure control from useForm
rules={{ required: true }}
render={({ field }) => <Input {...field} />}
/>
import { Controller } from "react-hook-form";

// In your form
<Controller
name="name"
control={control} // Make sure to destructure control from useForm
rules={{ required: true }}
render={({ field }) => <Input {...field} />}
/>
Don't forget to destructure control from useForm:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
setValue,
control, // Add this
watch,
formState: { errors },
} = useForm<IPost, HttpError>({
// ...
});
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
setValue,
control, // Add this
watch,
formState: { errors },
} = useForm<IPost, HttpError>({
// ...
});
Why You Still Need useEffect You still need the useEffect hook because of how the form initialization works in Refine with React Hook Form. The form doesn't automatically populate fields from the query result in edit mode without manual intervention. This is a known limitation mentioned in several GitHub issues, including #1729 where users reported similar problems with form initialization in edit mode. The useEffect approach you're using is the recommended workaround:
useEffect(() => {
if (queryResult?.data?.data) {
setValue('name', queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
useEffect(() => {
if (queryResult?.data?.data) {
setValue('name', queryResult.data.data.name);
// Set other fields as needed
}
}, [queryResult?.data?.data, setValue]);
Alternative Approach If you want to avoid using useEffect, you could try using the defaultValues function with async:
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
control,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: 'your-id-here',
},
defaultValues: async () => {
// This will be called when the form is initialized
const response = await dataProvider.getOne({
resource: "proposal",
id: 'your-id-here',
});
return response.data;
},
});
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
control,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: 'proposal',
action: 'edit',
id: 'your-id-here',
},
defaultValues: async () => {
// This will be called when the form is initialized
const response = await dataProvider.getOne({
resource: "proposal",
id: 'your-id-here',
});
return response.data;
},
});
This approach might help avoid the need for the useEffect, but it's still addressing the same underlying issue with form initialization in edit mode. The need for these workarounds suggests there might be room for improvement in how Refine handles form initialization in edit mode with React Hook Form, especially when integrating with UI libraries like Ant Design. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
stormy-gold
stormy-goldOP3w ago
@kapa.ai the defaultValues approach seems better, but how come it doesn't do this under the hood? seems like such an oversight
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I understand your frustration. Based on the knowledge sources, there does seem to be a gap in how the useForm hook handles initialization in edit mode, particularly with React Hook Form integration. The issue appears to be related to race conditions between different data fetching methods. According to the documentation:
"When action is 'edit' or 'clone' a race condition with async defaultFormValues may occur. In this case, the form values will be the result of the last completed operation." Default Form Values Documentation
This race condition happens because: 1. When you set action: "edit", Refine automatically fetches data using its internal data provider 2. If you also provide defaultFormValues as an async function, both operations run concurrently 3. Whichever completes last will determine the final form values There have been several GitHub issues and Discord support threads about this problem. In one GitHub issue (#5727), a developer reported that defaultFormValues wasn't working with useDrawerForm, and the Refine team acknowledged this as a bug that needed fixing. In another Discord thread, a support staff member explained:
"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."
The recommended workaround from the support team is to disable the automatic fetching and use your own defaultValues function:
useForm({
refineCoreProps: { queryOptions: { enabled: false } },
defaultValues: async () => {
// Your data fetching logic
}
})
useForm({
refineCoreProps: { queryOptions: { enabled: false } },
defaultValues: async () => {
// Your data fetching logic
}
})
It does seem like an oversight in the API design that could be improved to make the developer experience more seamless, especially for such a common use case as editing existing records. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@680514070167683118> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?