モバイルとWebで複雑なフォームを構築することはソフトウェア開発でよくあることです。アカウントやプロフィール作成フローや、Eコマースチェックアウト、求人広告の掲載等、弊社で制作したアプリでもいろいろな方法で使ってきました。しかしながら、ユーザーインターフェースを構築するために有用なJavaScriptライブラリのReactで書かれたフォームはとても冗長になることがあり、アプリの他の部分に利用できる開発時間を費やしてしまうことになるかもしれませんし、保守性を損なう可能性もあります。

この記事ではFormikライブラリを使いこれらの課題を緩和することについて書きます。Formikでボイラープレート・コードを減らし、開発スピードを上げ、クライアントや開発者の満足度の向上につなげます。以下の例では、Webフォームですが、React Nativeで書かれたモバイルフォームでもFormikは使用することができます。

普通のReactで書いたフォーム

Reactを使い始めて最初に思ったことの一つは必要なボイラープレート・コードの量でした。これは特にフォームを構築するときに当てはまり、過度に冗長で繰り返しが多いです。例えば弊社で開かれたFormik関連の知識共有セッションで使用した下のフォームをご覧ください。

useStateの最初の使用法としてフォームの初期値を設定しています。エラーメッセージを備えるためにstateを使用し、またフォームが提出されているかを確認するのにも使用しています。入力やユーザーが入力フィールドをクリックし、離れることで値を更新したりエラーを発生させるいくつものイベントハンドラがあります。さらにはフォーム提出のイベントハンドラもあり、誤ってフォームの多重提出を防ぐと同時に、フォームで提出された値をテキストストリングに変更し、アラートボックス内でそれを表示します。

Form before refactoring to use Formik

Validation errors for unformiked form

上記のものも完全に使えるフォームではありますが、すべてのイベントハンドラ、バリデーションロジックを書かなくてはならず、特に入り組んだフォームではかなりの時間がかかります。より少ないコードではメンテナンスが楽になり、開発スピードも上がるため、弊社としてもクライアントにとってもいいことであります。機能には影響せずそれができると言ったらどうしますか?

Formikの使用

Reactでフォームを構築する際に冗長さを緩和するライブラリはいくつも存在しますが、特にダウンロード数でも圧倒的に人気なのはFormikです。GitHubリポジトリで述べられているように、Formikは値、バリデーション、提出の扱いなどのフォームのstateに対応しています。Formikのコンポーネントを用いてフォームをネスト化し、Formikを利用でき、値、エラー、イベントハンドラをコンポーネントのpropsを通し表示できます。

さらに、Formikは様々なバリデータとも使用でき、特にYupではプロセスが簡略化できます。自身のカスタムイベントハンドラとバリデーションの代わりにFormikとYupを使用することにより、フォームを動かすためのコードを何行も削減できます。

この記事の重要な点として、どのようにFormikで簡略化できるか見るために上の例のコードをリファクタリングしてみましょう。

まず、Formikのコンポーネントにフォームを入れ、入れ子構造にします。

初期値をuseStateで定義する代わりにFormikのコンポーネントのinitialValues propを使用できます。

state管理の一部をFormikに指名しました。次に、カスタムのhandleChangehandleSubmitイベントハンドラを取り除き、Formikのイベントハンドラを代わりに使用しましょう。

もしこれをブラウザでテストしているのであれば、もうすでにサブミットボタンが動作しないことに気づくでしょう。コンソールをチェックし、次のエラーを確認します:

Uncaught (in promise) TypeError: _this.props.onSubmit is not a function.

Formikでは独自のonSubmitのpropを指定できます。これはFormikのhandleSubmitイベントハンドラで呼び出されます。元々の機能を再現するためにそのpropを利用してみましょう。すなわち、文字列化されたJavaScriptオブジェクトとしてアラートボックス内にフォームの値を表示することです:

フォームが再び動きました。

次はバリデーションに取り掛かってみましょう。バリデーションを発動させるためにonBlurイベントアトリビュートを使用しているので、まずはhandleBlurイベントハンドラをFormikのそれで入れ替えてみましょう。

これにより、名前のついた入力ごとにpropが得られ、ユーザーが入力の選択を解除(“touched”) したときに、ユーザーが有効なデータを入力せずに入力からタブを離した際にエラーメッセージを表示できるようになります (たとえば、メールアドレスの形式が正しくない場合)。しかし、それに行く前にhandleBlurイベントハンドラを消したときにすべてのカスタムバリデーションは取り除かれたので、Formikとバリデータライブラリを使用することによりそれらを取り替え、単純化することができます。

Yupでバリデーションを行う

YupはFormikの制作者に気に入られ、その実装を簡単にするため彼はvalidationSchemaという特別なpropを制作しました。入力と前述したpropに値を渡すためバリデーションエラーとYupスキーマオブジェクトを定義することでYupを追加できます。(Yupのスキーマオブジェトを定義する際に沢山の選択肢がありますが、単純化のため元のフォームに類似するバリデーションだけを再現しました。)

エラーメッセージを表示する前に入力エリアにカーソルがクリックされたかどうかをチェックする必要があります。そうでなければ、値を入力中にエラーメッセージを表示したり、インプットフィールドから離れると誤ってエラーメッセージを2つの入力に表示してしまうためです。

エラーを把握するためにFormikコンポーネントを使用しているので、onSubmitを更新してisSubmittingも設定する場合は、useStateへのすべてのリファレンスを取り除くことができます。

最後に、Formikのhelperコンポーネントを使いさらにコードをきれいにしましょう。以下のhelperを使用します。

  • Field  入力名アトリビュートに基づいた適切なhandleChangehandeBlurイベントハンドラを受け取る
  • ErrorMessage 入力エリアにカーソルがクリックされたかどうか、エラーがあるかの両方をチェックする
  • Form onSubmitを受け取る

Form refactored to use Formik

これで簡単なリファクタリングのエクササイズは終わりになります。ファイルは100行ちょっとからほとんど半分の60行にまで減りました。そして、読みやすさを損なわずにそれを行うことができました。実際、読みやすくなり、短くシンプルになりました。コードをできるだけシンプルに保つことで開発スピードを上げ、保守性が上がるようにしました。

上の例はかなりシンプルですが、Formikから得られるメリットをご理解いただけたら幸いです。Reactを使用することで生じるすべてのフォーム関連の問題に正しいソリューションというわけではないですが、面倒を減らす役に立ちます。まだ試していなかったらぜひFormikを次のプロジェクトで試してみてはいかがでしょうか?

一緒に働きませんか?

プロジェクトに関するご相談やご質問など、お気軽にお問い合わせください。