Controller does not working for MUI Select and React Hook Form. TypeError: props.render is not a function - reactjs

I have parent and child components. In the child component, I have a 'select' and 'textfield'. I need to configure validation for this select - "Required". Textfield works, but select does not. I could not do it and searched to solve this and saw "Controller". But when I send control (useForm) as a prop to my child component, the app does not work and my error is TypeError: props.render is not a function
Parent component:
const { register, handleSubmit, control, formState: { errors }} = useForm();
<AddProductInfo register={register} control={control} errors={errors} />
Child component:
<FormControl fullWidth>
<Typography variant={"span"} className={styles.select}>
Category
</Typography>
<Controller
as={
<Select
value={addProductData.category}
onChange={handleChange}
// {...register("category", {
// required: "Choose one of them",
// })}
// error={Boolean(errors.category)}
>
{categoriesData.map((data, key) => (
<MenuItem key={key} value={data.id}>
{data.name.az}
</MenuItem>
))}
</Select>
}
name={"category"}
control={control}
></Controller>
<FormHelperText error variant="filled">
{errors.category?.message}
</FormHelperText>
</FormControl>

I solved. The right code
from
<Controller as={
<Select>
</Select>
}
/>
to
<Controller render={({ field }) => (
<Select {...field}>
</Select>
)}
/>
Like this
<Controller
render={({ field }) => (
<Select
{...field}
>
{categoriesData.map((data, key) => (
<MenuItem key={key} value={data.id}>
{data.name.az}
</MenuItem>
))}
</Select>
)}
name={"category"}
control={control}
></Controller>

Related

Reset values in react hook forms with fields loaded in map

So i have a form that has dropdowns for companies and as many companies they are i want the values to be loaded in there. I know how to do it if its not in a map.
customerCompanyList.map(cList => {
return (
<Controller
name={`customerCompany-${cList.id}`}
control={control}
render={({ field }) => (
<FormControl
variant="outlined"
className="mt-16"
>
<InputLabel id="customerCompany-type-label">
Customer Company
</InputLabel>
<Select
labelId="customerCompany-type-label"
id="customerCompany"
label="Customer Company"
{...field}
fullWidth
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{companyList.map(item => {
return (
<MenuItem key={item.id} value={item.id}>
{item.name}
</MenuItem>
);
})}
</Select>
{errors?.type?.customerCompany && (
<FormHelperText>
{errors.type.customerCompany}
</FormHelperText>
)}
</FormControl>
)}
/>
);
})
What im stuck is how do i reset the values
im using useEffect to reset the values for the ones that are not in a map. but this one i cant figure out
reset({
isCustomer: customerCompanies.length > 0,
isResource: resourceCompanies.length > 0,
isStaff: roles.length > 0,
staffRoles: roles.length > 0 ? roles : null
});

RHF not resetting values in an MUI checkbox group

I am trying to reset my form with the reset() method and, it is working partially. All of the form's fields get reset, except for the checkboxes group.
I have tried three different ways of implementing an MUI CheckboxesGroup but, I always get the same result.
You can see a working example in this sandbox
Checkbox one implementation
<FormControl error={error} required={required} component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<Controller
name={name}
render={({ field: { onBlur: rhfonBlur, onChange: rhfonChange } }) => (
<FormGroup onBlur={rhfonBlur}>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox onChange={(e) => rhfonChange(handleCheck(key))} />
}
/>
);
})}
</FormGroup>
)}
control={control}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox two implementation
<FormControl error={error}>
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
key={key}
control={<Checkbox {...register(name)} />}
value={key}
label={key}
/>
);
})}
</FormGroup>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox three implementation
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange: rfhonChange, value } }) => {
// console.log("value:", value);
return (
<FormGroup>
{Object.keys(state).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
onChange={(e) => {
rfhonChange(handleChange(e));
}}
checked={state[key]}
name={key}
/>
}
/>
);
})}
</FormGroup>
);
}}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Your third implementation was the correct way of doing it by using RHF's <Controller /> component. The reason why it wasn't working was because of the following:
you're not updating RHF's internal state for your field as you are only passing a function to the onChange handler. There is no need to use an additional useState here
your initial value is a boolean (false) but you are using an object inside the <CheckboxThree /> component
So basically you can simplify the component to the following (in you're example it isn't clear if the field value should be an object or an array containing the selected options - so the example i made is using an array):
const CheckboxThree = ({
control,
error,
helperText,
label,
name,
options
}) => {
return (
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange, value, ref, ...field } }) => (
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
{...field}
name={key}
checked={value.some((option) => option === key)}
onChange={(event, checked) => {
if (checked) {
onChange([...value, event.target.name]);
} else {
onChange(
value.filter((value) => value !== event.target.name)
);
}
}}
inputRef={ref}
/>
}
/>
);
})}
</FormGroup>
)}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
);
};

react-hook-form with react select

Someone has a working sample with a react-hook-form with a react-select? In below the Select with id="accountId" works. However I need it to be a required field. I tried adding:
innerRef={register({ required: true })}
But that did not work. AFAIK this is because the Select needs to be wrapped in a Controller (correct me if I am wrong).
So I tried adding the Controler where id="accountId2". But now I get error:
Uncaught TypeError: Cannot read property 'split' of undefined
I am looking for a small sample where the Select will be integrated with the form and required fields.
<Container>
<Form onSubmit={handleSubmit(onSubmit)}>
<FormGroup>
<div>
<Controller
as={<Select
name="accountId2"
id="accountId2" />}
options={options}
control={control}
/>
</div>
</FormGroup>
<FormGroup>
<Label for="exampleCheckbox">Choose account to update</Label>
<div>
<Select
name="accountId"
id="accountId"
innerRef={register({ required: true })}
isDisabled={isNewAccount}
ref={selectInputRef}
isClearable={true}
placeholder="Search for an existing account number or click new account below"
label="Single select"
options={options}
defaultValue=""
/>
</div>
Yes, In order for Select to work with RHF, you need to wrap it in a controller like this.
<Controller
as={
<Select>
{options.map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</Select>
}
name={options_name}
control={control}
defaultValue=""
rules={{ required: true }}
/>
So it worked for me by adding the following attribute to the controller.
rules={{ required: true }}
Hope this answers your question.
If you are using react-hook-form: "^7.19.1", can be used as below.
<Controller
control={control}
name="test"
render={({
field: { onChange, onBlur, value, name, ref },
fieldState: { invalid, isTouched, isDirty, error },
formState,
}) => (
<Select
onBlur={onBlur}
onChange={onChange}
inputRef={ref}
className={classes.textField}
fullWidth
input={<Input id="name" />}
defaultValue={"science"}
>
{tags.map((tag, index) => (
<MenuItem key={index} value={tag}>
{tag}
</MenuItem>
))}
</Select>
)}
/>

React Final Form and Material UI, disabling element when specific radio button is selected

New to React Final Form and creating a form with Material UI. I have a date picker disabled by default and want to enable it when a certain radio is selected. I am unsure how to get the form to enable the picker when the 'schedule' radio button is selected and disable it when not.
My Radio group component:
<RadioGroup defaultValue={defaultVal} name={name}>
{items.map(item => (
<React.Fragment key={item.value}>
<FormControlLabel
name={item.name}
label={item.label}
value={item.value}
control={
<Field
name={name}
type="radio"
render={({ input: { name, label, value, onChange } }) => (
<Radio
name={name}
label={label}
value={value}
onChange={onChange}
/>
)}
/>
}
/>
</React.Fragment>
))}
</RadioGroup>
Form Component:
const items=[
{
name: 'send',
value: 'immediately',
label: 'Immediately
},
{
name: 'send',
value: 'schedule',
label: 'Schedule
}
]
<Form onSubmit={onSubmit}>
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Schedule items={items} />
...
</form>
)}
</Form>
My Schedule Component:
<RadioGroup name="options" defaultValue="immediately" items={items}/>
<TimePicker disabled={true} />
You need to get radio field state in Schedule component. Check this link from final form docs: https://codesandbox.io/embed/pyrwplknom?codemirror=1

Using React Hook Form with Material UI Select Component

I've been having trouble finding the correct way to use React Hook Form with certain Material UI components. I can get it to work with simple Text Fields but when it comes to nested components I can't figure it out.
Specifically, I am trying to submit the data from the selection in a Select component with child MenuItems.
See the notes in the code:
export default function NewGoalPane() {
const classes = useStyles();
const {register, handleSubmit} = useForm();
return (
<div className={classes.root}>
<CssBaseline />
<form noValidate onSubmit={handleSubmit((data) => alert(JSON.stringify(data)))}>
<main className={classes.content}>
<div className={classes.text_field_section}>
//This text field works and React Hook Form reads the data correctly.
<TextField
label="Goal Title"
name="goalTitle"
defaultValue=""
inputRef={register}/>
</div>
//This Select component does not read the data from the selection of the MenuItems.
<div className={classes.section}>
<Select
label="Repeating"
name="repeating"
defaultValue={true}
inputRef={register} // Here I call register like all the React Hook Form docs say
>
<MenuItem value={true}>Yes</MenuItem>
<MenuItem value={false}>No</MenuItem>
</Select>
</div>
</main>
</form>
</div>
);
}
How do I fix the Select component so that React Hook Form collects the data in the form submission?
I found Material UI's TextField simple as it requires less code and also you can avoid using controller and Select component. This is my solution.
<TextField
select
name: 'city'
inputRef={register({ required: true })}
onChange={e => setValue('city', e.target.value, {shouldValidate: true})}
label="City"
defaultValue="">
{cityList.map((option, index) => (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
))}
</TextField>
{errors.city && <ErrorText>City is required</ErrorText>}
If you are using v7 the best way was to use controllers for Material Ui components
import { useForm, Controller } from 'react-hook-form';
//component
const methods = useForm();
const { control } = methods;
<Controller
name="city"
control={control}
defaultValue=""
rules={{ required: 'City Required' }}
render={({ field: { onChange, value } }) => (
<TextField
fullWidth
label="City"
variant="outlined"
value={value}
onChange={onChange}
/>
)}
/>

Resources