【React】React Hook Formでフォーム分割して送信時にデータ結合する

金子

2024.01.04

286

明けましておめでとうございます。


inputを分割して送信時にデータを結合する方法がドキュメントや記事には書かれていなかったので

紹介したいと思います。

【React】ReactHookFormのinputを分割して送信時に結合する。

ReactHookFormとは?

そもそもReactHookFormって何?と思う方もいるかもしれませんので簡単に説明すると

フォームに対してバリデーションをかけてくれるReact専用ライブラリです。


公式でも拡張機能に優れていると自負していますが、

バリデーションで言うとresolverオプションにZodYup などの別のバリデーションライブラリを設定することができ、

muiなどのコンポーネントライブラリと組み合わせることもできます。


muiと組み合わせるとこんな感じです。(muiはデフォルトでアニメーションがついてるのでかっこいいです!

赤文字がReact Hook Formのバリデーション部分となります。


公式のデモからも実際にフォームを入力して確かめることができます。

React Hook Form


input分割の利点

フォーム分割の利点はユーザの入力工数を減らせることにあります。

数字入力の桁数が多い場合

分割なし

1232343454565676

  • 何桁?
  • どこを間違えたか分かりづらい


入力が分割されている場合

1232 3434 5456 5676


日付などの間に/が必要な場合

分割なし

YY/MM

  • /までユーザに入力させている。


入力が分割されている場合

YY MM



実装

では本題に移っていきます。


まず完成をお見せします。

"use client";

import React, { useState } from "react";
import { useForm, SubmitHandler, Controller } from "react-hook-form";

type FormData = {
  birthday: string;
};

export default function page() {
  const [isValue, setIsValue] = useState("");
  const {
    control,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>();


  const onSubmit: SubmitHandler<FormData> = (data) => {
    setIsValue(data.birthday);
  };


  return (
    <div className="flex justify-center items-center w-screen h-screen flex-col">
      {isValue && (
        <p className="mb-8 text-lg">
          あなたの生年月日は<span className="text-blue-600">{isValue}</span>
          です。
        </p>
      )}
      <form onSubmit={handleSubmit(onSubmit)} className="flex w-50 h-50 p-12 bg-gray-400 gap-8 flex-col">
        <div>
          <label>生年月日</label>
          <Controller
            name="birthday"
            control={control}
            rules={{ required: true }}
            render={({ field }) => (
              <div className="flex !w-2/4 items-center">
                <input
                  className="h-6"
                  type="text"
                  name="Year"
                  placeholder="年"
                  onChange={(e) => {
                    const yearValue = e.target.value;
                    const monthValue = field.value?.split("/")[1];
                    const dayValue = field.value?.split("/")[2];
                    const newBirthdayValue = `${yearValue}/${monthValue}/${dayValue}`;
                    field.onChange(newBirthdayValue);
                  }}
                  maxLength={4}
                />
                <p className="mx-2">/</p>
                <input
                  className="h-6"
                  type="text"
                  name="Mouth"
                  placeholder="月"
                  onChange={(e) => {
                    const yearValue = field.value?.split("/")[0];
                    const monthValue = e.target.value;
                    const dayValue = field.value?.split("/")[2];
                    const newBirthdayValue = `${yearValue}/${monthValue}/${dayValue}`;
                    field.onChange(newBirthdayValue);
                  }}
                  maxLength={2}
                />
                <p className="mx-2">/</p>
                <input
                  className="h-6"
                  type="text"
                  name="Day"
                  placeholder="日"
                  onChange={(e) => {
                    const yearValue = field.value?.split("/")[0];
                    const monthValue = field.value?.split("/")[1];
                    const dayValue = e.target.value;
                    const newBirthdayValue = `${yearValue}/${monthValue}/${dayValue}`;
                    field.onChange(newBirthdayValue);
                  }}
                  maxLength={2}
                />
              </div>
            )}
          />
          {errors.birthday && <p className="text-red-600">{errors.birthday.message}</p>}
        </div>
        <button className="h-10" type="submit">
          送信
        </button>
      </form>
    </div>
  );
}



大事な部分はonChangeです。

                 onChange={(e) => {
                    const yearValue = e.target.value;
                    const monthValue = field.value?.split("/")[1];
                    const dayValue = field.value?.split("/")[2];
                    const newBirthdayValue = `${yearValue}/${monthValue}/${dayValue}`;
                    field.onChange(newBirthdayValue);
                  }}


この部分で結合してfieldに返してます。

const newBirthdayValue = `${yearValue}/${monthValue}/${dayValue}`;
field.onChange(newBirthdayValue);



送信してみると

/区切りになってますね。

まとめ

ReactHookFormのcontrollerを複数用意して個別にバリデーションをかけたり、

結合された値にバリデーションをかけたりできるので用途に応じて柔軟に利用できるところがいいですね。


それでは、次回の記事でお会いしましょう!

この記事をシェアする