flat-fuchsia
flat-fuchsia9mo ago

Forms with custom components

Hello! I'm most likely being dumb but I'm currently working on making a create form based off of what was generated by the inferencer. I've made a custom component that I'd like to be a part of the create form:
const AbilityScoreIncrease: React.FC<AbilityScoreIncreaseProps> = ({ control, rules, abilities }) => {

return (
<Stack direction={'row'} spacing={2}>
{abilities.map((ability) => (
<Controller
key={ability}
control={control}
name={ability}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
))}
</Stack>
);
};
const AbilityScoreIncrease: React.FC<AbilityScoreIncreaseProps> = ({ control, rules, abilities }) => {

return (
<Stack direction={'row'} spacing={2}>
{abilities.map((ability) => (
<Controller
key={ability}
control={control}
name={ability}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
))}
</Stack>
);
};
and here is it implemented in the form:
<Controller
name="modifiers.AbilityScoreIncrease"
control={control}
rules={{ required: "This field is required" }}
render={({ field }) => (
<AbilityScoreIncrease
control={control}
abilities={["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]}
/>
)}
/>
<Controller
name="modifiers.AbilityScoreIncrease"
control={control}
rules={{ required: "This field is required" }}
render={({ field }) => (
<AbilityScoreIncrease
control={control}
abilities={["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]}
/>
)}
/>
What should I do to troubleshoot this?
16 Replies
kapa.ai
kapa.ai9mo ago
Hi @quandrix_. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Hmm, I don't know enough to give you a confident answer yet. However, based on the information provided, it seems like you might be having issues with the useForm hook and the control prop. In refine, useForm is a hook used to manage forms. It returns the necessary properties and methods to control the form. The control prop is used to control the form's state and actions. You should ensure that the form instance returned by useForm is connected to your Form element. If you're using a library like Ant Design, you would typically pass the form instance as a prop to the Form component. Here's a basic usage of the useForm hook from the refine blog:
const {
control,
handleSubmit,
watch,
formState: { errors },
} = useForm({
mode: "onChange",
defaultValues,
});
const {
control,
handleSubmit,
watch,
formState: { errors },
} = useForm({
mode: "onChange",
defaultValues,
});
You can find more information about this in the refine documentation (https://refine.dev/docs). Also, Refine uses Ant Design's Form component for form management. To ensure that your custom input fields are properly integrated with Refine's form management system, you need to follow these steps: 1. Your custom input fields should be wrapped within the Form.Item component from Ant Design. This allows the form management system to recognize your custom input fields. 2. Your custom input fields should be controllable components. This means they should accept a value prop and trigger an onChange event when the value changes. This allows the form management system to control the value of your custom input fields and listen for changes. 3. If your custom input field uses a different prop name for the value or a different event for changes, you can specify these using the valuePropName and trigger props of the Form.Item component. You can find more information about this in the refine documentation (https://refine.dev/docs) and refine blog (https://refine.dev/blog). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@223544018016010240> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia9mo ago
@kapa.ai I'm using material UI, not ant
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In refine, you can use Material UI for your forms and manage them using the @refinedev/react-hook-form adapter. This allows you to use all the features of the React Hook Form library in your refine project in a compatible way. Here's an example of how to use it in a create form from the refine blog:
import {
Box,
TextField,
Autocomplete,
useAutocomplete,
Create,
} from "@refinedev/mui";
import { useForm, Controller } from "@refinedev/react-hook-form";

export const PostCreate: React.FC = () => {
const {
refineCore: { formLoading },
saveButtonProps,
register,
control,
formState: { errors },
} = useForm<IPost, HttpError, IPost & { category: ICategory }>();

return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", { required: "Title is required" })}
error={!!errors?.title}
helperText={errors.title?.message}
margin="normal"
required
fullWidth
id="title"
label="Title"
name="title"
autoFocus
/>
<Controller
control={control}
name="category"
rules={{ required: "Category is required" }}
render={({ field }) => (
<Autocomplete
{...autocompleteProps}
{...field}
import {
Box,
TextField,
Autocomplete,
useAutocomplete,
Create,
} from "@refinedev/mui";
import { useForm, Controller } from "@refinedev/react-hook-form";

export const PostCreate: React.FC = () => {
const {
refineCore: { formLoading },
saveButtonProps,
register,
control,
formState: { errors },
} = useForm<IPost, HttpError, IPost & { category: ICategory }>();

return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
{...register("title", { required: "Title is required" })}
error={!!errors?.title}
helperText={errors.title?.message}
margin="normal"
required
fullWidth
id="title"
label="Title"
name="title"
autoFocus
/>
<Controller
control={control}
name="category"
rules={{ required: "Category is required" }}
render={({ field }) => (
<Autocomplete
{...autocompleteProps}
{...field}
In your case, you can use the Controller component from @refinedev/react-hook-form to wrap your custom AbilityScoreIncrease component. This will allow you to control the form state and handle validation rules. For more information, you can refer to the refine documentation (https://refine.dev/docs/examples/form/mui/useForm/) and refine blog (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@223544018016010240> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia9mo ago
@kapa.ai can you give me an example of using a custom component that is passed as a field for the create form?
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Sure, based on the information provided in the extracts, here's an example of how you can use a custom component in a create form using refine and Material UI: First, define your custom component. In this case, let's call it MyCustomComponent:
const MyCustomComponent = ({ value, onChange }) => {
// Your custom input logic here
};
const MyCustomComponent = ({ value, onChange }) => {
// Your custom input logic here
};
Then, in your create form, you can use the Controller component from @refinedev/react-hook-form to wrap your custom component. This will allow you to control the form state and handle validation rules:
import { Create, Box, Button } from "@refinedev/mui";
import { useForm, Controller } from "@refinedev/react-hook-form";

const { saveButtonProps } = useForm();

function UserCreate() {
return (
<Create saveButtonProps={saveButtonProps}>
<Box>
<Controller
name="myCustomField"
control={control}
rules={{ required: "This field is required" }}
render={({ field }) => (
<MyCustomComponent {...field} />
)}
/>
</Box>
</Create>
);
}

export default UserCreate;
import { Create, Box, Button } from "@refinedev/mui";
import { useForm, Controller } from "@refinedev/react-hook-form";

const { saveButtonProps } = useForm();

function UserCreate() {
return (
<Create saveButtonProps={saveButtonProps}>
<Box>
<Controller
name="myCustomField"
control={control}
rules={{ required: "This field is required" }}
render={({ field }) => (
<MyCustomComponent {...field} />
)}
/>
</Box>
</Create>
);
}

export default UserCreate;
In this example, MyCustomComponent is used as a custom input field in the create form. The Controller component from @refinedev/react-hook-form is used to control the form state and handle validation rules for the custom input field. For more information, you can refer to the refine documentation (https://refine.dev/docs/examples/form/mui/useForm/) and refine blog (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@223544018016010240> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia9mo ago
@kapa.ai When I hit the save button in the create component, nothing is happening when I have the custom component added. When I remove the component, the form correctly submits.
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Based on the information provided in the extracts, it seems like there might be an issue with how your custom component is handling form submission. In refine, form submission is typically handled by the handleSubmit method from the useForm hook. This method is passed to the onSubmit event handler of the form. Here's an example from the refine blog:
const {
handleSubmit,
} = useForm();

const handleSubmission = (data) => console.log(data);

<form onSubmit={handleSubmit(handleSubmission)}>
...
</form>
const {
handleSubmit,
} = useForm();

const handleSubmission = (data) => console.log(data);

<form onSubmit={handleSubmit(handleSubmission)}>
...
</form>
In this example, handleSubmit is passed the handleSubmission function, which is called when the form is submitted. If your custom component is preventing the form from being submitted, it might be because it's not correctly handling the onChange event or the value prop. Your custom component should accept a value prop and trigger an onChange event when the value changes. This allows the form management system to control the value of your custom input fields and listen for changes. Here's an example of a custom component that correctly handles the onChange event and the value prop:
const MyCustomComponent = ({ value, onChange }) => {
// Your custom input logic here
};
const MyCustomComponent = ({ value, onChange }) => {
// Your custom input logic here
};
If you're still having issues, it might be helpful to check the console for any error messages or warnings. These could provide more information about what's going wrong. For more information, you can refer to the refine documentation (https://refine.dev/docs/examples/form/mui/useForm/) and refine blog (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@223544018016010240> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia9mo ago
const AbilityScoreIncrease: React.FC<AbilityScoreIncreaseProps> = ({ control, rules, abilities }) => {

return (
<Stack direction={'row'} spacing={2}>
{abilities.map((ability) => (
<Controller
key={ability}
control={control}
name={ability}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
))}
</Stack>
);
};
const AbilityScoreIncrease: React.FC<AbilityScoreIncreaseProps> = ({ control, rules, abilities }) => {

return (
<Stack direction={'row'} spacing={2}>
{abilities.map((ability) => (
<Controller
key={ability}
control={control}
name={ability}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
))}
</Stack>
);
};
@kapa.ai This is the component I am using
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Your AbilityScoreIncrease component seems to be correctly set up with the Controller component from @refinedev/react-hook-form. However, the issue might be with how you're using this component in your form. When you use the AbilityScoreIncrease component in your form, you're setting the name prop to "modifiers.AbilityScoreIncrease". However, inside your AbilityScoreIncrease component, you're setting the name prop of each Controller to the ability value. This means that the names of your fields are not correctly nested under "modifiers.AbilityScoreIncrease". To fix this, you should concatenate "modifiers.AbilityScoreIncrease" with the ability value to correctly nest your fields. Here's how you can do it:
<Controller
name={`modifiers.AbilityScoreIncrease.${ability}`}
control={control}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
<Controller
name={`modifiers.AbilityScoreIncrease.${ability}`}
control={control}
rules={rules}
defaultValue={0}
render={({ field }) => (
<TextField
{...field}
id={`${ability}`}
type="number"
label={ability.charAt(0).toUpperCase() + ability.slice(1)}
onChange={(event) => {
field.onChange(event); // Update form state
}}
/>
)}
/>
This way, the names of your fields will be "modifiers.AbilityScoreIncrease.strength", "modifiers.AbilityScoreIncrease.dexterity", etc., which correctly nests them under "modifiers.AbilityScoreIncrease". For more information, you can refer to the refine documentation (https://refine.dev/docs/examples/form/mui/useForm/) and refine blog (https://refine.dev/blog/build-admin-panel-with-material-ui-and-strapi/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai9mo ago
<@223544018016010240> kapa.ai is still learning and improving, please let us know how it did by reacting below
flat-fuchsia
flat-fuchsia9mo ago
@kapa.ai I'm now able to submit correctly. However when submitting it is putting the ability scores in the root of the reuqest body instead of inside the modifiers.abilityscoreincrease object
kapa.ai
kapa.ai9mo ago
Let me check the knowledge base for you. One moment! :books: @quandrix_ That's a great question, but unfortunately I am only capable of responding to a maximum of five questions per thread. Don't hesitate to create a new thread for any further questions you have though. I'm here to help!