-
Hi I was trying to create a Select Controlled select component using MUI and react hook form passing from the use of FormProvider and useFormContext in the nested custom Select but by passing App.tsx // Ext
import {
Controller,
SubmitHandler,
useForm,
FormProvider,
} from "react-hook-form";
import {
Box,
Button,
Card,
CardActions,
CardContent,
MenuItem,
TextField,
Typography,
} from "@mui/material";
import { zodResolver } from "@hookform/resolvers/zod";
// Types
import { LoginFormInputs } from "../types";
// Scss
import "../static/scss/Login.scss";
import BrandName from "../components/BrandName";
import { LoginSchema } from "../schemas";
import FormSelect from "../components/form/FormSelect";
interface LoginFomrInputs {
selectDefVal: string;
}
const Login: React.FC = () => {
const defaultValues: LoginFormInputs = {
selectDefVal: "",
};
const methods = useForm<LoginFormInputs>({
resolver: zodResolver(LoginSchema),
defaultValues,
});
const submitAuth: SubmitHandler<LoginFormInputs> = async (
data: LoginFormInputs
) => {
console.log(data);
};
return (
<Box className="login-itemsbox">
<Box className="title-box">
<Typography>
<BrandName />
</Typography>
</Box>
<Box className="card-form-box">
<FormProvider {...methods}>
<Card component="form" onSubmit={methods.handleSubmit(submitAuth)}>
<CardContent>
<FormSelect name="Testing" label="Test: *" />
</CardContent>
<CardActions>
<Button type="submit" variant="contained">
Login
</Button>
</CardActions>
</Card>
</FormProvider>
</Box>
<Box className="small-footer-box">Small Footer</Box>
</Box>
);
};
export default Login; Nested custom select component import { Controller, useFormContext } from "react-hook-form";
import {
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
SelectProps,
} from "@mui/material";
import React from "react";
import { LoginFormInputs } from "../../types";
interface FormSelectPropsT {
name: string;
label: string;
}
const test = "Option 1";
const FormSelect: React.FC<FormSelectPropsT & SelectProps> = ({
name,
label,
...selectProps
}: FormSelectPropsT) => {
const {
control,
formState: { errors },
} = useFormContext<LoginFormInputs>(); // Specify the type it seems necessary otherwise it gives errors, even if should be passed by the context
return (
<Controller
name={name} // if I leave the FormSelectPropsT name as string it gives the type error if I modify it to be: name: "selectDefVal" the error disappears
control={control}
render={({ field }) => (
<FormControl>
<InputLabel id={label}>
{label}
</InputLabel>
<Select
{...field}
{...selectProps}
value={field.value}
className="select"
style={{ width: 230 }}
labelId={label}
label={label}
onChange={(e) => {
field.onChange(e);
}}
error={!!errors[name]}
>
<MenuItem key={test} value={test}>
{test}
</MenuItem>
</Select>
<FormHelperText sx={{ color: "red" }}>
{errors[name] ? errors[name]?.message : ""}
</FormHelperText>
</FormControl>
)}
/>
);
};
export default FormSelect; The type error is: So if I say the type of name should be "selectDefVal" the error disappears, but I thought that the type should be automatically passed from the |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
Also if I change my custom form select component like this: import { Controller, useFormContext } from "react-hook-form";
import {
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
SelectProps,
} from "@mui/material";
import React from "react";
import { LoginFormInputs } from "../../types";
interface FormSelectPropsT {
name: string;
label: string;
}
const test = "Option 1";
const FormSelect: React.FC<FormSelectPropsT & SelectProps> = ({
name,
label,
...selectProps
}: FormSelectPropsT) => {
const {
control,
formState: { errors },
} = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field }) => (
<FormControl>
<InputLabel id={label}>
{label}
</InputLabel>
<Select
{...field}
{...selectProps}
value={field.value}
className="select"
style={{ width: 230 }}
labelId={label}
label={label}
onChange={(e) => {
field.onChange(e);
}}
error={!!errors[name]}
>
<MenuItem key={test} value={test}>
{test}
</MenuItem>
</Select>
<FormHelperText sx={{ color: "red" }}>
{errors[name] ? errors[name]?.message : ""} // The error now It's here
</FormHelperText>
</FormControl>
)}
/>
);
};
export default FormSelect; I get this error: PS: My objective is to have a customizable select component to be used in different forms. |
Beta Was this translation helpful? Give feedback.
-
If all you need is the field <Controller render={({ fieldState }) => fieldState.error} /> |
Beta Was this translation helpful? Give feedback.
-
I've found my logical error! import { Controller, useFormContext } from "react-hook-form";
import {
TextField,
TextFieldProps,
} from "@mui/material";
import React from "react";
interface FormSelectPropsT {
name: string;
label: string;
}
const RhfTextFieldMui: React.FC<FormSelectPropsT & TextFieldProps> = ({
name,
label,
...selectProps
}: FormSelectPropsT) => {
const {
control,
} = useFormContext();
return (
<Controller
name={name}
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
{...selectProps}
value={field.value}
className="textfield"
style={{ width: 230 }}
label={label}
onChange={(e) => {
field.onChange(e);
}}
error={!!fieldState?.error}
helperText={
fieldState?.error
? fieldState?.error?.message
: ""
}
>
</TextField>
)}
/>
);
};
export default RhfTextFieldMui; |
Beta Was this translation helpful? Give feedback.
I've found my logical error!
The error was that I was using the errors in the
formState
and not thefieldState
when I've defined my custom element, this way the react fragment is no more needed. The new code is: