【React】react-hook-form-muiを使ったフォームの作成

# 概要
npmで公開されている[react-hook-form-mui](https://www.npmjs.com/package/react-hook-form-mui)を使ってフォームを作ってみます。このライブラリは通常のreact-hook-form + MUIよりもシンプルにコーディングできるようで、npmのダウンロード数を見ると意外と利用されているような印象を持ちます。

https://www.npmjs.com/package/react-hook-form-mui

以前にreact-hook-formとMUIを使ってフォームを作ったことがありますので、このコードと比較していきたいと思います。

https://qiita.com/StephenTorres/items/85aeb5d9f737532c9011
# 前提
・Reactがインストールされていること
・MUIがインストールされていること
```
npm i @mui/material @emotion/react @emotion/styled
```
・react-hook-formとreact-hook-form-muiがインストールされていること
```
npm i react-hook-form react-hook-form-mui
```

# 使い方
Readmeドキュメントに記載されている通り、以下のコンポーネントが用意されていますので、これらを使ってコーディングしていくイメージかと思います。
今回の記事は一部のみ使います。
- FormContainer
- AutocompleteElement
- TextFieldElement
- SelectElement
- MultiSelectElement
- RadioButtonGroup
- CheckboxButtonGroup
- CheckboxElement
- SwitchElement
- PasswordElement
- DatePickerElement
- SliderElement
- ToggleButtonGroupElement

# サンプルコード(最小限)
react-hook-form-muiが動作する必要最小限のコードです。
このコードをもとにカスタマイズをしていきます。
```typescript
"use client"
import { Button } from "@mui/material"
import React from "react"
import { FormContainer, TextFieldElement } from "react-hook-form-mui"

const page = () => {
  const submit = (data: object) => {
    console.log(data);
  }
  return (
      <FormContainer defaultValues={{ example: "" }} onSuccess={submit}>
        <TextFieldElement name="example" label="example" required/>
        <div>
          <Button type='submit' variant="contained">ログイン</Button>
        </div>
      </FormContainer>
  )
}

export default page
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/2c22d927-6f90-4aee-9aea-ab69f786ef53.png)
入力された値はsubmitメソッドの第１引数に引き渡されます。
メソッドの変数名は任意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/e73c51eb-6626-42ec-be67-0d38a90eef1f.png)



比較としてreact-hook-form-muiを使わないほうのサンプルコードを以下に記載します。ほぼ同じ動作をしますが、シンプルになっていますかね
```typescript
"use client"
import { Button, TextField } from "@mui/material"
import React from "react"
import { SubmitHandler, useForm } from "react-hook-form"

type Input = {
  example: string
}

const page = () => {
  const { register, handleSubmit } = useForm<Input>()
  const onSubmit: SubmitHandler<Input> = (data) => console.log(data);
  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <TextField
          id="outlined-basic"
          label="Outlined"
          {...register("example")}
        />
        <div>
          <Button type='submit' variant="contained">Contained</Button>
        </div>
      </form>
    </>
  )
}

export default page
```

# オプション
MUIをもとにコンポーネントが作られているようで、sxやMUIが用意したスタイルを適用することはできるようです。
```typescript
    <TextFieldElement 
        name="example" 
        label="example" 
        variant="standard" //MUIのText Fieldより
        sx={{ width: "600px" }}  //スタイルの適用
    />
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/82c01a55-d363-4f63-8c05-e700221f7956.png)

### エラーメッセージの設定
入力されていなかった場合などのエラーメッセージはすでに用意されていますが、自分でメッセージを設定したい場合は、FormErrorProviderを使うことで設定が可能です。
```typescript
"use client"
import { Button } from "@mui/material"
import React from "react"
import { FormContainer, FormErrorProvider, TextFieldElement } from "react-hook-form-mui"

const page = () => {
  const submit = (data: object) => {
    console.log(data);
  }
  const errorPrams = (error: FieldError) => {
    if (error.type === 'required') {
      return '入力してください。';
    }
    return error?.message;
  }
  return (
    <FormErrorProvider onError={errorPrams}>
      <FormContainer defaultValues={{ example: "" }} onSuccess={submit}>
        <TextFieldElement 
          name="example"
          label="example"
          required
        />
        <div>
          <Button type='submit' variant="contained">ログイン</Button>
        </div>
      </FormContainer>
    </FormErrorProvider>
  )
}

export default page
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/86152118-114a-4599-af4f-79a51c7ea785.png)

# 完成したもの
```typescript
"use client"
import { Box, Button, Container, Paper } from "@mui/material"
import React from "react"
import { FormContainer, PasswordElement, TextFieldElement } from "react-hook-form-mui"

const page = () => {
  const submit = (data: object) => {
    console.log(data);
  }
  return (
    <>
      <Container sx={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh" }}>
        <Paper elevation={3} sx={{ width: "600px", padding: 4 }}>

          <Box sx={{ textAlign: "center", fontSize: "24px", fontWeight: "bold" }}>ログイン</Box>
          <FormContainer defaultValues={{ email: "", password: "" }} onSuccess={submit}>
            <TextFieldElement
              name="email"
              label="Email"
              fullWidth
              required
              type="email"
              margin="normal"
            />
            <PasswordElement
              name="password"
              label="Password"
              fullWidth
              required
              margin="normal"
            />
            <div>
              <Button type='submit' variant="contained" sx={{ marginTop: "16px" }}>ログイン</Button>
            </div>
          </FormContainer>

        </Paper>
      </Container>
    </>
  )
}

export default page
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/ded86a47-54af-40ac-874c-8e451e61ef2a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/cc73f7c0-eed3-4aeb-8696-552e04d0b224.png)
### 比較
react-hook-form-muiを使わなかった場合のコードを比較として記載します。
```typescript
"use client"
import { Box, Button, Container, Paper, TextField } from "@mui/material"
import React from "react"
import { SubmitHandler, useForm } from "react-hook-form"

type Input = {
  email: string,
  password: string
}

const page = () => {
  const { register, handleSubmit, formState: { errors }} = useForm<Input>()
  const onSubmit: SubmitHandler<Input> = (data) => console.log(data);
  return (
    <>
      <Container sx={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh" }}>
        <Paper elevation={3} sx={{ width: "600px", padding: 4 }}>

          <Box sx={{ textAlign: "center", fontSize: "24px", fontWeight: "bold" }}>ログイン</Box>
          <form onSubmit={handleSubmit(onSubmit)}>
            <TextField
              fullWidth
              id="outlined-basic"
              label="Email"
              type="email"
              margin="normal"
              {...register("email", { required: true })}
              error={errors.email ? true : false }
              helperText={errors.email ? "Email is required" : ""}
            />

            <TextField
              fullWidth
              id="outlined-basic"
              label="Password"
              type="password"
              margin="normal"
              {...register("password", { required: true })}
              error={errors.password ? true : false }
              helperText={errors.password ? "Password is required" : ""}
            />
            <div>
              <Button type='submit' variant="contained" sx={{ marginTop: "16px" }}>ログイン</Button>
            </div>
          </form>
          
        </Paper>
      </Container>
    </>
  )
}

export default page

```

# まとめ
シンプルにコーディングできる分、エラー処理などのこだわった機能を追加するのは難しそうな印象を持ちました。
特にこだわりがなくシンプルに使いたい場合は、このライブラリは選択肢一つとしてよさそうかと思います。

# 参考
https://react-hook-form-material-ui.vercel.app/