🌀

React+MUI v5 の 入力フォーム用のライブラリは React Hook Form の 一択

2022/03/23に公開

今回は React(Typescript) + MUI(v5) の フォームライブラリをどれにしようか調査しました。

入力フォーム用のライブラリ

React の入力フォーム用のライブラリをどれにするか?
2022年03月22日時点で主要なもの、だいたい以下の3点のようです。

  1. Formik
  2. React Final Form
  3. React Hook Form

React Hook Form に決めた経緯

まずは npm trends で比較してみます。
人気順は Formik > React Hook Form > React Final Form のようです。

1. Formik

npm trends を確認すると、一番人気があるライブラリです。
ただし、他の2つのライブラリと比較して、レンダリングが多くパフォーマンスに劣るようです。
現在あえて選択することはないかと思うのでパスします。
パフォーマンスの比較

2. React Final Form

3つの中で最も軽量なライブラリです。
レンダリングは抑えられていてパフォーマンスも良いようなのですが、あまり人気はないようです。
ホームページは、親切とはいいがたく、サンプルも少ないです。
https://final-form.org/react

3. React Hook Form

レンダリングが抑えられていてパフォーマンスが良いです。
Formikの次に人気があり、日本語記事もたくさんヒットします。
ホームページは、説明文は少ないですが、サンプルコードは多いです。
https://react-hook-form.com/jp/

コードを書き比べてみた

UIフレームワークは MUI v5 を使用すると決めています。
そこで、 MUI v5 を使用したコードを React Final Form と React Hook Form のそれぞれで書き比べてみました。

MUI v5 と React.useState を使った実装

まずは React Final Form と React Hook Form を使用しないで実装してみます。
トテモメンドクサイデス。

InputForm.tsx
import React from 'react'
import { Stack, TextField, Button } from '@mui/material'

export function InputForm() {
  const [name, setName] = React.useState('')
  const [hasNameError, setHasNameError] = React.useState(false)

  const inputName = React.useCallback(
    (event) => {
      const inputValue = event.target.value
      const isEmpty = inputValue === ''
      setName(inputValue)
      setHasNameError(isEmpty)
    },
    [setName, setHasNameError]
  )

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    const isEmptyName = name === ''

    if (isEmptyName) {
      setHasNameError(true)
    }
    console.log(`Sybmit Name: ${name}`)
  }

  return (
    <Stack component="form" noValidate onSubmit={handleSubmit} spacing={2} sx={{ m: 2, width: '25ch' }}>
      <TextField
        type="text"
        label="名前"
        required
        value={name}
        error={hasNameError}
        onChange={inputName}
        helperText={hasNameError ? '名前を入力してください。' : ''}
      />
      <Button variant="contained" type="submit">
        送信する
      </Button>
    </Stack>
  )
}

MUI v5 と React Final Form を使った実装

つづいて MUI v5 と React Final Form を使って実装します。
React Final Form のインストール

npm install final-form react-final-form

サンプルコードです。
ホームページのサンプルコードだけでは書き方がわかりませんでした。
検索でも情報があまりヒットせず、書き方を調べるのに時間がかかりました。
react-final-form の Fieldコンポーネントの render に @mui/material のTextField を指定するような書き方をするようです。
個人的な感想ですが、あまり直観的な書き方ではないと感じました。

InputReactFinalForm.tsx
import { Form, Field } from 'react-final-form'
import { Stack, TextField, Button } from '@mui/material'

type Values = {
  name: string
}

export function InputReactFinalForm() {
  const onSubmit = (values: Values) => {
    console.log(`onSubmit:${values.name}`)
  }

  const validateName = (value: string) => {
    if (!value) {
      return '名前を入力してください。'
    }
    return undefined
  }

  return (
    <Form
      onSubmit={onSubmit}
      initialValues={{ name: 'longbridgeyuk' }}
      render={({ handleSubmit }) => (
        <Stack component="form" noValidate onSubmit={handleSubmit} spacing={2} sx={{ m: 2, width: '25ch' }}>
          <Field
            name="name"
            validate={validateName}
            render={({ input, meta }) => (
              <TextField
                {...input}
                placeholder="名前"
                label="名前"
                helperText={meta.error}
                error={meta.touched && !!meta.error}
              />
            )}
          />

          <Button variant="contained" type="submit">
            送信する
          </Button>
        </Stack>
      )}
    />
  )
}

MUI v5 と React Hook Form を使った実装

つづいて MUI v5 と React Hook Form を使って実装します。
React Hook Form のインストール

npm install react-hook-form

サンプルコードです。
こちらはホームページのサンプルコードだけでサクッと実装できました。
個人的な感想として、コードもシンプルでわかりやすいと感じました。
ただし、MUI の TextField の場合は、React Hook Form がサクッとフィットしましたが、他のコンポーネントだとここまでサクッとはいかないようです。

2022/03/25 サンプルコード書き替えました。
React Hook Forms で MUIコンポーネントを簡単に扱うために用意されたラッパコンポーネントである Controller コンポーネントを使用して書くようです。

import { Stack, TextField, Button } from '@mui/material'
import { useForm, SubmitHandler, Controller } from 'react-hook-form'

type Inputs = {
  name: string
}

export function InputReactHookFormTextField() {
  const {
    control,
    handleSubmit,
    formState: { errors }
  } = useForm<Inputs>({
    defaultValues: { name: 'longbridgeyuk' }
  })

  const validationRules = {
    name: {
      required: '名前を入力してください。',
      minLength: { value: 4, message: '4文字以上で入力してください。' }
    }
  }

  const onSubmit: SubmitHandler<Inputs> = (data: Inputs) => {
    console.log(`submit: ${data.name}`)
  }

  return (
    <Stack component="form" noValidate 
    onSubmit={handleSubmit(onSubmit)} 
    spacing={2} sx={{ m: 2, width: '25ch' }}>

      <Controller
        name="name"
        control={control}
        rules={validationRules.name}
        render={({ field }) => (
          <TextField
            {...field}
            type="text"
            label="名前"
            error={errors.name !== undefined}
            helperText={errors.name?.message}
          />
        )}
      />
      <Button variant="contained" type="submit">
        送信する
      </Button>
    </Stack>
  )
}

まとめ

React Hook Form は ホームページのサンプルコードが多いので、導入しやすいと感じました。
また人気もあるので記事の量も多く、何か困ったことがあっても検索すれば解決しやすいです。
コードもシンプルで理解しやすいと思いました。
React(Typescript) + MUI(v5) であれば、React Hook Form の一択かなと思います。

Discussion