React Hook FormとMUI, react-number-formatを組み合わせた入力部品を作る

投稿日: April 23, 2022

はじめに

昨今のReactのフォームライブラリではスタンダードになっているReact Hook Form (v7) と、これもReactのプロジェクトではよく用いられるMUI (v5)の各種入力部品を組み合わせて、汎用的に使用できる入力部品を作ってみた。テキストの入力においては純粋なテキスト (string型) の入力と、数値 (number型) の入力を分けたくなるのが常なので、MUIの TextField をベースに、MuiTextFieldMuiNumberField の2つを用意してこれらを区別した。また、数値の入力部品では入力値をフォーマットして表示したい場面も多いので、MuiNumberField にはreact-number-formatも組み込んで、入力値のフォーマットができるようにした。

執筆時の依存バージョン

  • React v18.0.0
  • MUI v5.6.2
  • React Hook Form v7.30.0
  • react-number-format v4.9.1

先に最終的なデモを貼っておく。

GitHubリポジトリは以下。

現状形になっている部品一覧

ここからはインターフェース設計の思想、この部品を作る中で得た各ライブラリに対する情報や、実装で引っかかった点をまとめていく。

インターフェース

インターフェースの概要

インターフェースを紹介するために、一番シンプルな MuiTextField (string型のテキスト入力部品) のテンプレート部分のみ紹介する。(実際に動作する完成コードについては冒頭のGitHubリポジトリを参照。) 他の部品も同じインターフェースになっている。

<MuiTextField> は型引数としてフォーム全体の型を取るようにした。TypeScriptでReact Hook Formを使う場合は必ず定義することになるので、それをそのまま渡せば良い。ここで型を渡すと、name のPropsに渡した文字列がフォームのkeyとして存在するかどうかチェックされる。

MuiTextFieldの使用例
<MuiTextField<FormData> name='name' control={control} rules={{ required: '必須項目です。', maxLength: { value: 5, message: '5文字以内で入力してください。' } }} config={{ displayErrorMessage: true }} muiProps={{ textFieldProps: { label: '名前', fullWidth: true } }} />

インターフェースの設計思想

  • とにかくPropsが多いので、3つの大きなまとまりで分けて管理するようにした。一つ目は今回主役となっているReact Hook FormのProps、2つ目はMUIのComponentのProps、3つ目が部品を提供する側が独自で組み込みたいオプションを提供するためのPropsである。
    • React Hook FormのPropsは UseControllerProps としてライブラリから提供されている。このPropsはインターフェースとして最表面に出ていて欲しい(ネストして欲しくない)ので、今回作る部品のPropsはこの型との交差型にすることにした。
    • MUIのPropsは部品によっては複数必要になる。例えばAutocompleteではAutocomplete自体のPropsに加えて、内部に持つTextFieldのProps(フォームの幅やラベルなど)も渡せるようにしたい。よって、これらはオブジェクトにまとめて、PropsのmuiPropsという口で渡せるようにした。React Hook FormのPropsとMUIのPropsは同名のものが多く衝突する可能性が高い(そして意図せず衝突するとバグる)ので、最初から分けてバグを生む可能性を排除した。
    • 独自で組み込みたいオプションを提供するためのPropsについても、MUIのPropsと同様、オブジェクトにまとめて、configという口で渡せるようにした。react-number-formatのProps (NumberFormatPropsBase) は意味的にはここに収まるのが良いと思ったので、MuiNumberField のconfigは NumberFormatPropsBase との交差型になっている。

React Hook Form

(作成中...)

MUI

(作成中...)

react-number-format

(作成中...)


share on...