react-hook-formで入力内容によってバリデーションする方法

react-hook-formはReact用のフォームライブラリです。簡単に入力フォームなどの実装ができます。
必須項目などの簡単バリデーションの他に、入力内容によってバリデーションすることも可能です。本記事ではreact-hook-formでのバリデーションの設定方法について解説しています。

https://react-hook-form.com/

## react-hook-formのインストールなど
インストールのコマンドは下記の通りです。
```
npm install react-hook-form
#OR
yarn add react-hook-form 
```

## react-hook-formの基本的なバリデーション
react-hook-formには下記などの良く利用するバリデーションが最初から用意されています。

- required: 必須
- maxLength: 最大文字数
- minLenght: 最小文字数
- pattern: パターン（メールアドレスや正規表現などのパターンで使える）

それぞれをフォームで設定するだけで簡単に利用できます。

```tsx
<input
  {...register('name', {
    required: '必須入力',
    maxLength: {
      value: 50,
      message: '最大50文字です'
    },
    pattern:
      /^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]+.[A-Za-z0-9]+$/
  })}
  type="text"
/>
```

## 入力内容によってフォームをバリデーションする方法
続いて入力内容によってフォームをバリデーションする方法になります。コード全体は下記です。

```tsx
import dayjs from 'dayjs'
import { useForm } from 'react-hook-form'

type Form = {
  name: string
  email: string
  text: string
  check: boolean
}

const Form = () => {
  const {
    register,
    handleSubmit,
    formState: { errors }
  } = useForm<Form>()

  const submit = (data: Form) => {
    console.log(Form)
  }

  return (
    <>
      <form onSubmit={handleSubmit(submit)}>
        <div>
          <input
            {...register('name', {
              required: '必須入力',
              maxLength: {
                value: 50,
                message: '最大50文字です'
              }
            })}
            type="text"
          />
          {errors.name && <span>{errors.name.message}</span>}
        </div>
        <div>
          <input
            {...register('email', {
              required: '必須入力',
              maxLength: {
                value: 50,
                message: '最大50文字です'
              }
            })}
            type="email"
          />
          {errors.email && <span>{errors.email.message}</span>}
        </div>
        <div>
          <textarea
            {...register('text', {
              required: '必須入力',
              // 入力内容によってバリデーションをする
              validate: (data) => {
                if (data === 'hoge') {
                  return 'NGワードです'
                }
                if (!dayjs(data).isValid()) {
                  return '日付の形式が違います'
                }
              }
            })}
          ></textarea>
          {errors.text && <span>{errors.text.message}</span>}
        </div>
        <div>
          <input
            {...register('check', {
              required: '必須入力'
            })}
            type="checkbox"
          />
          {errors.check && <span>{errors.check.message}</span>}
        </div>
        <div>
          <input type="submit" value="送信" onSubmit={handleSubmit(submit)} />
        </div>
      </form>
    </>
  )
}

export default Form
```

ユーザーの入力した値によって細かにバリデーションする場合は、`validete`に設定します。
`return`でエラーメッセージを返すことでエラーとして表示されます。
`validate`のコールバック関数の引数には、フォームに入力された値が入ってきます。
値によってバリデーションをして返すエラーを変えたりできます。
例えばDay.jsなどの日付用のライブラリを使って日付のチェックなども可能です。

```js
validate: (data) => {
  if (!dayjs(data).isValid()) {
    return '日付の形式が違います'
  }
}
```

## MUI(Material UI)とreact-hook-formでの実装例
ReactのUIフレームワークであるMUIとreact-hook-formを組み合わせと時の実装例になります。

```tsx
import { Box, Button, Container, TextField } from '@mui/material'
import dayjs from 'dayjs'
import { Controller, useForm } from 'react-hook-form'

type Form = {
  name: string
  email: string
  text: string
  check: boolean
}

const Form = () => {
  const { control, handleSubmit } = useForm<Form>()

  const submit = (data: Form) => {
    console.log(data)
  }

  return (
    <Container>
      <Box component="form" onSubmit={handleSubmit(submit)}>
        <Box mb={4}>
          <Controller
            name="name"
            control={control}
            defaultValue=""
            rules={{
              required: { value: true, message: '必須入力です' },
              validate: (data) => {
                if (data === 'hoge') {
                  return 'NGワードです'
                }
                if (!dayjs(data).isValid()) {
                  return '日付の形式が違います'
                }
              }
            }}
            render={({ field, formState: { errors } }) => (
              <TextField
                {...field}
                fullWidth
                label="テキスト"
                error={errors.name ? true : false}
                helperText={errors.name?.message as string}
              />
            )}
          />
        </Box>
        <Box textAlign="right">
          <Button variant="contained" onClick={handleSubmit(submit)}>
            送信
          </Button>
        </Box>
      </Box>
    </Container>
  )
}

export default Form
```

![react-hooks-form-validate2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/3e642e11-21f9-71b8-7941-fc5533c31d09.png)

## 参考

https://react-hook-form.com/

https://mui.com/material-ui/getting-started/overview/