はじめに
フォームの作成において、あるチェックボックスのON/OFFを、他のチェックボックスにも反映させたい場面は珍しくないかと思います。
今回はFormikを使ったフォームの作成において、チェックボックスのチェックを他のチェックボックスにも反映させる方法を紹介します。
概要
「すべて選択」をチェックしたときには、「オプション1」〜「オプション3」までのチェックも連動して変わるようにしたいです。
実装方法
結論
先に結論から書くと、実装は以下のように行います。
// 「すべて」が選択されたときの処理
const handleSelectAll = (checked: boolean) => {
formik.setValues({
...formik.values,
selectAll: checked,
selectedOptions: checked ? options.map((option) => option.value) : [],
});
};
ポイントは2つです。
setValuesを使う
formik
のsetValues
は、引数に設定された値でフォーム全体の値を更新します。
これにより、関連する値をまとめて更新することができます。
スプレッド構文を使う
setValues
は設定された値で丸ごと更新してしまいます。
今回のように、複数のフォームが設定されている場面で、チェックボックスのみをsetValues
に指定してしまうと、他のフォームの値が消えてしまいます。
そこで使用するのがスプレッド構文です。
冒頭に示した例で言うところの...formik.values
がスプレッド構文にあたります。
formik.values
にはフォームのオブジェクトがすべて設定されているため、これを利用することで、フォームオブジェクトを取りこぼすことなく設定できます。
そして、その後にカンマ区切りで変更したいパラメータのみ設定します。
これにより、フォームのオブジェクトは維持しつつ、特定のプロパティのみ書き換えることができます。
このテクニックはformik
に限らず、オブジェクトを使う場面ならどこでも利用できます。
オブジェクトの特定のプロパティのみ書き換えたいときにはスプレッド構文を使うとよいです。
コード全容
今回の実装のすべてを以下に示します。
setValues
の他にも、getFieldProps
など、formik
を扱う上で便利なものを使っているのでぜひ確認してみてください。
また、フォームの構成においてはMaterial-UIも使用しています。
import { useFormik } from "formik";
import {
Checkbox,
FormControlLabel,
FormGroup,
Button,
TextField,
Radio,
RadioGroup,
} from "@mui/material";
// フォームの要素を型として定義
interface FormValues {
selectAll: boolean;
selectedOptions: string[];
textInput: string;
radioOption: string;
}
// チェックボックス用配列
const options = [
{ value: "option1", label: "オプション1" },
{ value: "option2", label: "オプション2" },
{ value: "option3", label: "オプション3" },
];
// ラジオボタン用配列
const radioOptions = [
{ value: "radio1", label: "ラジオ1" },
{ value: "radio2", label: "ラジオ2" },
{ value: "radio3", label: "ラジオ3" },
];
// フォームの初期値
const initialValues: FormValues = {
selectAll: false,
selectedOptions: [],
textInput: "",
radioOption: "",
};
const App = () => {
// formikの設定
const formik = useFormik({
initialValues,
onSubmit: (values) => {
console.log(values);
},
});
// 「すべて」の選択状態
const allSelected = formik.values.selectedOptions.length === options.length;
// 「すべて」が選択されたときの処理
const handleSelectAll = (checked: boolean) => {
formik.setValues({
...formik.values,
selectAll: checked,
selectedOptions: checked ? options.map((option) => option.value) : [],
});
};
return (
<form onSubmit={formik.handleSubmit}>
<TextField
fullWidth
label="テキスト入力"
{...formik.getFieldProps("textInput")}
margin="normal"
/>
<RadioGroup
aria-label="radio-options"
{...formik.getFieldProps("radioOption")}
>
{radioOptions.map((option) => (
<FormControlLabel
key={option.value}
value={option.value}
control={<Radio />}
label={option.label}
/>
))}
</RadioGroup>
<FormGroup>
<FormControlLabel
control={
<Checkbox
checked={allSelected}
onChange={(e) => handleSelectAll(e.target.checked)}
name="selectAll"
/>
}
label="すべて選択"
/>
{options.map((option) => (
<FormControlLabel
key={option.value}
control={
<Checkbox
{...formik.getFieldProps("selectedOptions")}
value={option.value}
checked={formik.values.selectedOptions.includes(option.value)}
/>
}
label={option.label}
/>
))}
</FormGroup>
<Button
type="submit"
variant="contained"
color="primary"
style={{ marginTop: "1rem" }}
>
送信
</Button>
</form>
);
};
export default App;
まとめ
単に画面上のチェックボックスをON/OFFするだけなら、state
でも実装は可能ですが、フォームに対して状態が変わったことを通知するためには今回のようにフォームに対して値を書き換えて上げる必要があります。
その方法の一つとしてsetValues
は一括での更新が可能なので、もっと他にも利用方法がありそうだと感じました。