React Hook Form v7でMaterial UI v4のCheckboxを制御

React
スポンサーリンク
language

React Hook FormでMaterial UIのCheckboxを制御でReact Hook Form v6でMaterial UIのCheckboxを制御する方法を紹介しました。

その後、React Hook Form v7がリリースされた。
React Hook Form v7でMaterial-UIを制御する方法が少し変わりましたので、Material UI v4のCheckboxを制御する方法をtypescriptでご紹介します。

今回ご紹介するCheckboxの制御仕様も前回と同じで以下の通りです。

  • 初期値(デフォルトチェック)に対応
  • チェックボックスの値の変更を制御
  • Material UIを使ってCheckboxグループを設置(同じnameで複数設置)

そして完成したチェックボックスのフォームが以下のフォームです。

スポンサーリンク

サンプルコード

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}
    </>
  );
}

要件を満たす制御の解説

制御する要件は以下の通りでした。
各要件に該当するコードの解説をします。

  • 初期値に対応
  • チェックボックスの値の変更を制御
  • Material UIを使ってCheckboxグループを設置(同じnameで複数設置)

初期値に対応

初期値に対応するところではReact Hook Formは使わずにできます。
Material-UIのCheckboxにデフォルトチェックを入れておく項目を配列に入れておきます。

const defaultCheckedValues= ["B",];

次のコードのdefaultValuesでReact Hook Form用に初期値を定義します。
defaultChecked.join(",")で配列をカンマ区切りの文字にしています。

 const {
    formState: { errors },
    control,
    setValue,
    getValues,
    handleSubmit
  } = useForm<FieldValues>({
    criteriaMode: "all",
    defaultValues: { checkbox: defaultCheckedValues.join(",") }
  });

次にMaterial-UIのために初期値を定義します。
CheckboxdefaultCheckeddefaultCheckedValuesに値が入っているか確認をします。
defaultCheckedValuesに値が入っていれば defaultCheckedtrueとなります。

<Checkbox defaultChecked={defaultCheckedValues.includes(option.title)} />

これでデフォルトチェックを入れておくことができます。

チェックボックスの値の変更を制御

チェックボックスに変更があった場合、handleCheck関数が実行されます。

handleCheck関数内では

  • 変更があった項目の値がすでにあれば削除
  • 変更があった項目の値がなければ値を追加

をしています。

  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(",");
  };

チェック済みの値を取得部分ではgetValues()でチェック済みの値を取得しています。
チェック済みの値がない場合はvaluesには空の配列が入ります。

空要素削除部分では、React Hook Form v7のgetValues()で取得した値には空の値が入っていることがあるので、それを除外しています。

useForm - getValues
Performant, flexible and extensible forms with easy-to-use validation.

Material UIのCheckboxグループを設置

Material UIのCheckboxを複数設置する場合はReact Hook Form v7のControllerrender内でチェックボックスグループ作ります。

React Hook Form v7のControllerごと複数生成しないように気をつけてください。

<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>

React Hook Form v7で制御しているMaterial-UIのCheckboxの値を取得して値を操作するにはFormControlLabelonChangeを記述する必要があります。

参考

useForm
Performant, flexible and extensible forms with easy-to-use validation.
TS Support
Performant, flexible and extensible forms with easy-to-use validation.

この記事でわからないコードなどがあれば、書籍で勉強するのがおすすめです。
プログラミングの技術の移り変わりは早いので最新版の書籍を購入することをお勧めします。
中古本を買って勉強したけど、今は使われない技術だったということはよくあります。

javascript・Reactでおすすめの書籍をご紹介します。

りーほー
りーほー

JavaScriptを基礎からしっかり学びたい方におすすめ!

りーほー
りーほー

JavaScriptを基礎から応用までガッツリ学びたい方におすすめ!

りーほー
りーほー

Reactの概念や仕組みを把握し、開発で使えるReactを学びたい方にオススメ!

タイトルとURLをコピーしました