Rio Blog

世界のどこかでゆるく生きるITエンジニアのブログ

React Hook Formでバリデーションを実装する

Yupのようなバリデーションライブラリを使わずとも、React Hook Form (RHF)で基本的なバリデーションは可能ですので、その方法を解説します。なお、UIライブラリとして、MUIを使っています。

シンプルなバリデーション

まずはログインや会員登録を想定したフォームを作成し、そこにバリデーションを実装していきます。

なお、RHFやMUIのインストールは、こちらの記事を参照してください。

rio-log.hatenablog.jp

コード全体はこんな感じです。

<App.tsx>

import { useForm } from 'react-hook-form'
import {
  Button, Checkbox, Container, FormControlLabel, FormGroup, FormHelperText, Stack, TextField
} from '@mui/material'

type formType = {
  name: string,
  email: string,
  agreement: boolean
}

export default function App() {

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

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

  return (
    <Container maxWidth="sm" sx={{ pt: 5 }}>
      <form onSubmit={handleSubmit(submit)}>
        <Stack spacing={3}>
          {/* TextField */}
          <TextField
            label="Name"
            {...register("name", { required: "error" })}
            error={errors.name ? true : false}
            helperText={errors.name?.message}
          />
          <TextField
            label="Email"
            {...register("email", { required: "error" })}
            error={"email" in errors}
            helperText={errors.email?.message}
          />

          {/* CheckBox */}
          <FormGroup>
            <FormControlLabel
              control={
                <Checkbox {...register("agreement", { required: "error" })} />
              }
              label="Agree to Terms and Conditions"
            />
            <FormHelperText error={errors.agreement ? true : false}>
              {errors.agreement?.message}
            </FormHelperText>
          </FormGroup>

          <Button type="submit" color="primary" variant="contained">
            Sign Up
          </Button>
        </Stack>
      </form>
    </Container>
  )
}

簡単にポイントを解説します。

// register内でエラー時のメッセージを指定し、
 {...register("name", { required: "error" })}

// helperTextでそれを表示
helperText={errors.name?.message}

// 以下は結果に違いはないので、どちらで表記するかは好みかと。
error={errors.name ? true : false}
error={"email" in errors}

ブラウザで確認

空の状態でボタンをクリックすると、エラーが表示されます。

関数化する

続いて、関数化して別ファイルから呼びます。

まずは下記のように register の中を書き換えて、インポート文も追加しましょう。

<App.tsx>

import { emailValidation, textValidation } from './Validation'

<TextField
   label="Name"
   {...register("name", { validate: textValidation })}
   error={errors.name ? true : false}
   helperText={errors.name?.message}
/>
<TextField
   label="Email"
   {...register("email", { validate: emailValidation })}
   error={"email" in errors}
   helperText={errors.email?.message}
/>

次に、Appt.tsxと同じ階層にValidation.tsというファイルを作成し、下記の通り記述します。入力されたデータを受け取って、エラーを返したりする関数ですね。

<Validation.ts>

const errorText = "error"
const maxLength = 255

// Validation for Text
export const textValidation = (value: string | null) => {
  if (!value) {
    return errorText
  } else if (value && value.length > maxLength) {
    return `Please enter within ${maxLength} characters.`
  }
}

// Validation for Email
export const emailValidation = (value: string | null) => {
  const regex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/
  if (!value) {
    return errorText
  } else if (!regex.test(value)) {
    return "Please enter a valid Email."
  }
}

ブラウザで確認

このように、条件に当てはまらない場合はエラーが表示できました。

おわりに

RHFのregisterのオプションはこれ以外にもあって、min(最小文字数)などももちろん設定可能です。詳しくは公式ドキュメントを見てくださいな。

複雑な条件でもない限り、RHFでバリデーションが可能なことはお判りいただけたかと思います。また、タイプミスを避けたりメンテナンス性を高めるために、関数化して読み込む方法は有効ですので、ぜひ試してみてください。