今回はReact Hook Form v7でMaterial UI v4のフォームを制御する方法をご紹介します。
Material UIのTextFieldやRadio、Selectは素直に動いてくれますが、Checkboxはちょっとしたコツ、工夫が必要です。
サンプルコードをご紹介しますので是非参考にしてください。
また、 React Hook Formからのフォームのエラーテキストを表示するサンプルコードも合わせてご紹介しています。
TextField
React Hook Form v7で制御したMaterial UI v4 のTextFieldは以下の通りです。
サンプルコードの通り、TextFieldは素直に動いてくれます。
TextFieldを制御するサンプルコードは以下の通りです。
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">テキスト質問</FormLabel>
<FormHelperText>
{errors?.text && errors?.text.message}
</FormHelperText>
<Controller
name="text"
control={control}
rules={{ required: "入力してください" }}
render={({ field }) => <TextField {...field} variant="outlined" />}
/>
</FormControl>
<Button type="submit" variant="contained" color="primary">
送信
</Button>
</form>
<br />
送信内容
<br />
{submitData}
</>
);
}
Checkbox
React Hook Form v7でMaterial UI v4 のCheckboxを制御するにはコツが要ります。
以下の React Hook Form v7で制御したMaterial UI v4 のCheckbox は次の仕様となっています。
Checkboxを制御するサンプルコードは以下のとおりです。
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); // 空要素削除
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">質問</FormLabel>
<FormHelperText>
{errors?.checkbox && errors?.checkbox.message}
</FormHelperText>
<FormGroup>
<Controller
name="checkbox"
control={control}
rules={{ required: "選択してください。" }}
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">
送信
</Button>
</form>
<br />
送信内容
<br />
{submitData}
</>
);
}
また、 React Hook Form v7でMaterial UI v4 のCheckboxを制御する方法は以下の記事で詳しく解説しています。
Select
React Hook Form v7で制御したMaterial UI v4 のSelectは以下の通りです。
サンプルコードの通り、Selectは素直に動いてくれます。
Selectを制御するサンプルコードは以下の通りです。
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">セレクト質問</FormLabel>
<FormHelperText>
{errors?.select && errors?.select.message}
</FormHelperText>
<Controller
name="select"
control={control}
rules={{ required: "選択してください。" }}
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">
送信
</Button>
</form>
<br />
送信内容
<br />
{submitData}
</>
);
}
RadioGroup
React Hook Form v7で制御したMaterial UI v4 のRadioGroupは以下の通りです。
サンプルコードの通り、 RadioGroupも素直に動いてくれます。
RadioGroupを制御するサンプルコードは以下のとおりです。
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">ラジオ質問</FormLabel>
<FormHelperText>
{errors?.radio && errors?.radio.message}
</FormHelperText>
<Controller
name="radio"
control={control}
rules={{ required: "選択してください。" }}
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">
送信
</Button>
</form>
<br />
送信内容
<br />
{submitData}
</>
);
}
まとめ
それぞれを1つのフォームにまとめると次のようにできます。
サンプルコードは以下のとおりです。
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); // 空要素削除
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">テキスト質問</FormLabel>
<FormHelperText>
{errors?.text && errors?.text.message}
</FormHelperText>
<Controller
name="text"
control={control}
rules={{ required: "入力してください" }}
render={({ field }) => <TextField {...field} variant="outlined" />}
/>
</FormControl>
<FormControl
required
error={errors?.hasOwnProperty("checkbox")}
component="fieldset"
fullWidth
style={{ marginBottom: "30px" }}
>
<FormLabel component="legend">質問</FormLabel>
<FormHelperText>
{errors?.checkbox && errors?.checkbox.message}
</FormHelperText>
<FormGroup>
<Controller
name="checkbox"
control={control}
rules={{ required: "選択してください。" }}
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">セレクト質問</FormLabel>
<FormHelperText>
{errors?.select && errors?.select.message}
</FormHelperText>
<Controller
name="select"
control={control}
rules={{ required: "選択してください。" }}
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">ラジオ質問</FormLabel>
<FormHelperText>
{errors?.radio && errors?.radio.message}
</FormHelperText>
<Controller
name="radio"
control={control}
rules={{ required: "選択してください。" }}
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">
送信
</Button>
</form>
<br />
送信内容
<br />
{submitData}
</>
);
}
Checkboxだけ制御するのに工夫が必要がですが、他は公式のサンプルコードの通り素直に動いてくれます。
React hook formを用いてMaterial UIのフォームを制御することで、見た目も美しくフォームの機能も充実します。
下記に参考書をまとめておきます。
JavaScriptを基礎からしっかり学びたい方におすすめ!
JavaScriptを基礎から応用までガッツリ学びたい方におすすめ!
Reactの概念や仕組みを把握し、開発で使えるReactを学びたい方にオススメ!
コメント