Controlling Material UI v4 Form with React Hook Form v7

programming
スポンサーリンク
language

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.

  • Support for initial values (default checks)
  • Control checkbox value changes
  • Set up Checkbox groups using Material UI (multiple checkboxes with the same name)

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.

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