In this article, I will show you how to control a Material UI v4 form with React Hook Form v7.
Material UI’s TextField, Radio, and Select work just fine, but Checkbox requires some tricks and ingenuity.
Here is a sample code for your reference.
It also includes a sample code to display the error text of a form from React Hook Form.
TextField
The TextField in Material UI v4 controlled by React Hook Form v7 is shown below.
As you can see in the sample code, the TextField works straightforwardly.
The sample code to control a TextField is as follows.
import React, { useState } from "react";
import { useForm, FieldValues, Controller } from "react-hook-form";
import Button from "@material-ui/core/Button";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
import TextField from "@material-ui/core/TextField";
export default function App() {
const [submitData, setSubmitData] = useState<string>("");
const {
formState: { errors },
control,
handleSubmit
} = useForm<FieldValues>({
criteriaMode: "all"
});
const handleFormOnSubmit = (data: any) => {
setSubmitData(data.text);
};
return (
<>
<form
method="POST"
onSubmit={handleSubmit(handleFormOnSubmit)}
encType="multipart/form-data"
>
<FormControl
required
error={errors?.hasOwnProperty("text")}
component="fieldset"
fullWidth
>
<FormLabel component="legend">Question(Text)</FormLabel>
<FormHelperText>
{errors?.text && errors?.text.message}
</FormHelperText>
<Controller
name="text"
control={control}
rules={{ required: "Please input." }}
render={({ field }) => <TextField {...field} variant="outlined" />}
/>
</FormControl>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
<br />
Submitted content.
<br />
{submitData}
</>
);
}
Checkbox
Controlling the Material UI v4 Checkbox with React Hook Form v7 is tricky.
The following Material UI v4 Checkbox controlled by React Hook Form v7 has the following specification.
The sample code to control the Checkbox is as follows
import React, { useState } from "react";
import { useForm, FieldValues, Controller } from "react-hook-form";
import Button from "@material-ui/core/Button";
import Checkbox from "@material-ui/core/Checkbox";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
export default function App() {
const [submitData, setSubmitData] = useState<string>("");
const defaultCheckedValues = ["B"];
// Options
const options = [
{
title: "A"
},
{
title: "B"
},
{
title: "C"
},
{
title: "D"
}
];
const {
formState: { errors },
control,
setValue,
getValues,
handleSubmit
} = useForm<FieldValues>({
criteriaMode: "all",
defaultValues: { checkbox: defaultCheckedValues.join(",") }
});
const handleFormOnSubmit = (data: any) => {
setSubmitData(data.checkbox);
};
const handleCheck = (
option: { title: string },
event: React.ChangeEvent<{}>
) => {
let values: string[] = getValues("checkbox")?.split(",") || [];
values = values.filter((v) => v); // empty element deletion
let newValues: string[] = [];
if ((event.target as HTMLInputElement).checked) {
newValues = [...(values ?? []), option.title];
} else {
newValues = values?.filter((value) => value !== option.title);
}
setValue("checkbox", newValues.join(","));
return newValues.join(",");
};
return (
<>
<form
method="POST"
onSubmit={handleSubmit(handleFormOnSubmit)}
encType="multipart/form-data"
>
<FormControl
required
error={errors?.hasOwnProperty("checkbox")}
component="fieldset"
fullWidth
>
<FormLabel component="legend">Question(Checkbox)</FormLabel>
<FormHelperText>
{errors?.checkbox && errors?.checkbox.message}
</FormHelperText>
<FormGroup>
<Controller
name="checkbox"
control={control}
rules={{ required: "Please make a selection." }}
defaultValue=""
render={({ field, fieldState }) => (
<>
{options.map((option, i) => (
<FormControlLabel
{...field}
key={i}
label={option.title}
onChange={(event) =>
field.onChange(handleCheck(option, event))
}
control={
<Checkbox
defaultChecked={defaultCheckedValues.includes(
option.title
)}
/>
}
/>
))}
</>
)}
/>
</FormGroup>
</FormControl>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
<br />
Submitted content.
<br />
{submitData}
</>
);
}
Also, how to control Material UI v4’s Checkbox with React Hook Form v7 is explained in detail in the following article.
Select
Here is the Select for Material UI v4 controlled by React Hook Form v7.
As you can see in the sample code, the Select works straightforwardly.
The sample code to control Select is as follows.
import React, { useState } from "react";
import { useForm, FieldValues, Controller } from "react-hook-form";
import Button from "@material-ui/core/Button";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
export default function App() {
const [submitData, setSubmitData] = useState<string>("");
// Options
const options = [
{
title: "A"
},
{
title: "B"
},
{
title: "C"
},
{
title: "D"
}
];
const {
formState: { errors },
control,
handleSubmit
} = useForm<FieldValues>({
criteriaMode: "all"
});
const handleFormOnSubmit = (data: any) => {
setSubmitData(data.select);
};
return (
<>
<form
method="POST"
onSubmit={handleSubmit(handleFormOnSubmit)}
encType="multipart/form-data"
>
<FormControl
required
error={errors?.hasOwnProperty("select")}
component="fieldset"
fullWidth
>
<FormLabel component="legend">Question(Select)</FormLabel>
<FormHelperText>
{errors?.select && errors?.select.message}
</FormHelperText>
<Controller
name="select"
control={control}
rules={{ required: "Please make a selection." }}
render={({ field, fieldState }) => (
<Select {...field}>
{options.map((option, i) => (
<MenuItem value={option.title} key={i}>
{option.title}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
<br />
Submitted content.
<br />
{submitData}
</>
);
}
RadioGroup
Here is the RadioGroup of Material UI v4 controlled by React Hook Form v7.
As you can see in the sample code, the RadioGroup works just fine.
The sample code to control RadioGroup is shown below.
import React, { useState } from "react";
import { useForm, FieldValues, Controller } from "react-hook-form";
import Button from "@material-ui/core/Button";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
export default function App() {
const [submitData, setSubmitData] = useState<string>("");
// Options
const options = [
{
title: "A"
},
{
title: "B"
},
{
title: "C"
},
{
title: "D"
}
];
const {
formState: { errors },
control,
handleSubmit
} = useForm<FieldValues>({
criteriaMode: "all"
});
const handleFormOnSubmit = (data: any) => {
setSubmitData(data.radio);
};
return (
<>
<form
method="POST"
onSubmit={handleSubmit(handleFormOnSubmit)}
encType="multipart/form-data"
>
<FormControl
required
error={errors?.hasOwnProperty("radio")}
component="fieldset"
fullWidth
>
<FormLabel component="legend">Question(Radio)</FormLabel>
<FormHelperText>
{errors?.radio && errors?.radio.message}
</FormHelperText>
<Controller
name="radio"
control={control}
rules={{ required: "Please make a selection." }}
render={({ field, fieldState }) => (
<RadioGroup {...field}>
{options.map((option, i) => (
<FormControlLabel
value={option.title}
control={<Radio />}
label={option.title}
key={i}
/>
))}
</RadioGroup>
)}
/>
</FormControl>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
<br />
Submitted content.
<br />
{submitData}
</>
);
}
conclusion
Each of these can be combined into one form as follows
The sample code is shown below.
import React, { useState } from "react";
import { useForm, FieldValues, Controller } from "react-hook-form";
import Button from "@material-ui/core/Button";
import Checkbox from "@material-ui/core/Checkbox";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormGroup from "@material-ui/core/FormGroup";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
import TextField from "@material-ui/core/TextField";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup";
export default function App() {
const [submitData, setSubmitData] = useState<string>("");
const defaultCheckedValues = ["B"];
// Options
const options = [
{
title: "A"
},
{
title: "B"
},
{
title: "C"
},
{
title: "D"
}
];
const {
formState: { errors },
control,
setValue,
getValues,
handleSubmit
} = useForm<FieldValues>({
criteriaMode: "all",
defaultValues: { checkbox: defaultCheckedValues.join(",") }
});
const handleFormOnSubmit = (data: any) => {
setSubmitData(
data.text + "、" + data.checkbox + "、" + data.select + "、" + data.radio
);
};
const handleCheck = (
option: { title: string },
event: React.ChangeEvent<{}>
) => {
let values: string[] = getValues("checkbox")?.split(",") || [];
values = values.filter((v) => v); // empty element deletion
let newValues: string[] = [];
if ((event.target as HTMLInputElement).checked) {
newValues = [...(values ?? []), option.title];
} else {
newValues = values?.filter((value) => value !== option.title);
}
setValue("checkbox", newValues.join(","));
return newValues.join(",");
};
return (
<>
<form
method="POST"
onSubmit={handleSubmit(handleFormOnSubmit)}
encType="multipart/form-data"
>
<FormControl
required
error={errors?.hasOwnProperty("text")}
component="fieldset"
fullWidth
style={{ marginBottom: "30px" }}
>
<FormLabel component="legend">Question(Text)</FormLabel>
<FormHelperText>
{errors?.text && errors?.text.message}
</FormHelperText>
<Controller
name="text"
control={control}
rules={{ required: "Please input." }}
render={({ field }) => <TextField {...field} variant="outlined" />}
/>
</FormControl>
<FormControl
required
error={errors?.hasOwnProperty("checkbox")}
component="fieldset"
fullWidth
style={{ marginBottom: "30px" }}
>
<FormLabel component="legend">Question(Checkbox)</FormLabel>
<FormHelperText>
{errors?.checkbox && errors?.checkbox.message}
</FormHelperText>
<FormGroup>
<Controller
name="checkbox"
control={control}
rules={{ required: "Please make a selection." }}
defaultValue=""
render={({ field, fieldState }) => (
<>
{options.map((option, i) => (
<FormControlLabel
{...field}
key={i}
label={option.title}
onChange={(event) =>
field.onChange(handleCheck(option, event))
}
control={
<Checkbox
defaultChecked={defaultCheckedValues.includes(
option.title
)}
/>
}
/>
))}
</>
)}
/>
</FormGroup>
</FormControl>
<FormControl
required
error={errors?.hasOwnProperty("select")}
component="fieldset"
fullWidth
style={{ marginBottom: "30px" }}
>
<FormLabel component="legend">Question(Select)</FormLabel>
<FormHelperText>
{errors?.select && errors?.select.message}
</FormHelperText>
<Controller
name="select"
control={control}
rules={{ required: "Please make a selection." }}
render={({ field, fieldState }) => (
<Select {...field}>
{options.map((option, i) => (
<MenuItem value={option.title} key={i}>
{option.title}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
<FormControl
required
error={errors?.hasOwnProperty("radio")}
component="fieldset"
fullWidth
style={{ marginBottom: "30px" }}
>
<FormLabel component="legend">Question(Radio)</FormLabel>
<FormHelperText>
{errors?.radio && errors?.radio.message}
</FormHelperText>
<Controller
name="radio"
control={control}
rules={{ required: "Please make a selection." }}
render={({ field, fieldState }) => (
<RadioGroup {...field}>
{options.map((option, i) => (
<FormControlLabel
value={option.title}
control={<Radio />}
label={option.title}
key={i}
/>
))}
</RadioGroup>
)}
/>
</FormControl>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
</form>
<br />
Submitted content.
<br />
{submitData}
</>
);
}
Only the Checkbox requires some effort to control, but the rest of the code works as shown in the official sample code.
By using React hook form to control the Material UI forms, we can make them look beautiful and enhance the form functionality.
コメント