react-hook-form:自作二択ボタン【サンプル付き】

react-hook-form:自作コンポーネント
◆ Live配信スケジュール ◆
サイオステクノロジーでは、Microsoft MVPの武井による「わかりみの深いシリーズ」など、定期的なLive配信を行っています。
⇒ 詳細スケジュールはこちらから
⇒ 見逃してしまった方はYoutubeチャンネルをご覧ください
【4/18開催】VSCode Dev Containersで楽々開発環境構築祭り〜Python/Reactなどなど〜
Visual Studio Codeの拡張機能であるDev Containersを使ってReactとかPythonとかSpring Bootとかの開発環境をラクチンで構築する方法を紹介するイベントです。
https://tech-lab.connpass.com/event/311864/

今回は、react-hook-formとTailwindを使用して自作のトグルボタンコンポーネントの作成について紹介していきます。Formの要望では様々なものがありますが、アンケートではトグルボタンが効果を発揮します。

初めに

どもども!こんにちはGWに富山に訪問中の龍ちゃんです。親戚の家があるので一年に一回必ず富山に来ているのですが、富山駅周辺の飲食店情報はだいぶ詳しくなりましたね。こっちに来ていると移動が多いので、合間を縫って文章を生成しています。と思ったら、GWが明けて二週間たってました。コードは書き上げていたのですが、あれよあれよと仕事が始まってゴリゴリSAN値削っている毎日です。冗談ですよw

今回は、前回の『react-hook-form』の題材を引き続き第二弾となります。今回は、Tailwindで作成した自作コンポーネントを利用してトグルボタンを作成していきたいと思います。トグルボタンと言っても設定系のフォームによく使われているものではなく、【持っている】・【持っていない】を判別するためのフォームになります。

今回のブログで分かるようになる内容としては以下になります。

  • Controllerを使用して自作のコンポーネントを適応方法
  • boolean型の自作コンポーネントの作成のやり方

になります。

それでは初めて行きましょう。

Formデザイン

Figmaでトグルボタンのデザインを作ってみました。単純に二択を迫るボタンですね。正確にはボタンが二個あるだけで、トグルボタンではないのです。(トグルボタンとは?)デザイン的なものを作成すれば今回の方法でトグルボタンを作成することができます。

実装前イメージ

コーディング

『react-hook-form』で自作のコンポーネントを使用する方法としては、公式ドキュメントによると、【Controller】を使う方法と【useController】を使用する二つの方法あります。僕が書いている方法だと二つの方法の合いの子のような記法になっています。今回自作したコンポーネント名は【FormToggleButton】になります。Propsとして以下を親コンポーネントから受け取っています。

  • value:フォームの説明文(例:コンテンツ1)
  • state:フォームの値(例:boolean→True)
  • onChange:フォームを変更するための関数

Typescriptを使用しているので、各変数に型定義を用意しています。

それではコードです。react-hook-formの定義をしている親コンポーネントが以下になります。

import { useForm, SubmitHandler, Controller } from 'react-hook-form';

export const HookFormTest4 = () => {
  type Machines = {
    id: number;
    name: string;
  };
  const QuestionnaireList: Machines[] = [
    { id: 0, name: 'コンテンツ1' },
    { id: 1, name: 'コンテンツ2' },
    { id: 2, name: 'コンテンツ3' },
    { id: 3, name: 'コンテンツ4' },
  ];
  const { handleSubmit, control } = useForm<{ [id: string]: boolean }>({
    mode: 'onSubmit',
    defaultValues: {
      コンテンツ1: false,
      コンテンツ2: false,
      コンテンツ3: false,
      コンテンツ4: false,
    },
  });

  const onSubmit: SubmitHandler<{ [id: string]: boolean }> = (formData: {
    [id: string]: boolean;
  }) => {
    console.log(formData);
  };
  return (
    <>
      <form className="flex w-full flex-col gap-10" onSubmit={handleSubmit(onSubmit)}>
        <div className="flex flex-col gap-4 rounded-lg border p-10">
          <div className="flex flex-col gap-2">
            <h4 className="text-center text-sm">以下の機器をお持ちですか?</h4>
          </div>
          {QuestionnaireList.map((value) => (
            <Controller
              control={control}
              name={value.name}
              key={value.id}
              render={({ field }) => (
                <FormToggleButton
                  onChange={field.onChange}
                  state={field.value}
                  value={value.name}
                />
              )}
            />
          ))}
        </div>
        <div className="flex justify-end">
          <button
            className={
              'inline-flex h-16 w-64 items-center justify-center rounded-lg border border-[#2D34CC] bg-[#2D34CC] text-white'
            }
          >
            送信
          </button>
        </div>
      </form>
    </>
  );
};

フォームの内容を表示するコンポーネントが以下になります。

import { AiOutlineCheck } from 'react-icons/ai';

type Props = {
  value: string;
  state: boolean;
  onChange: (value: boolean) => void;
};

const FormToggleButton = (props: Props) => {
  const { state, onChange, value } = props;
  const onClickTrue = () => {
    onChange(true);
  };
  const onClickFalse = () => {
    onChange(false);
  };
  return (
    <>
      <div className="inline-flex w-full grow items-center justify-between">
        <div className="inline-flex w-[121px] gap-4">
          <span className="flex items-center">{value}</span>
        </div>
        <div className="inline-flex w-full max-w-xl flex-row justify-end gap-4">
          <button
            type="button"
            className={
              'inline-flex h-16 w-64 items-center justify-center rounded-lg border ' +
              (state
                ? 'bg-[#2D34CC] border-[#2D34CC] text-white'
                : 'bg-white text-[#777777] border-[#777777]')
            }
            onClick={onClickTrue}
          >
            <AiOutlineCheck className={state ? 'inline-block' : 'hidden'} />
            持っている
          </button>
          <button
            type="button"
            className={
              'inline-flex h-16 w-64 items-center justify-center rounded-lg border ' +
              (!state
                ? 'bg-[#2D34CC] border-[#2D34CC] text-white'
                : 'bg-white text-[#777777] border-[#777777]')
            }
            onClick={onClickFalse}
          >
            <AiOutlineCheck className={!state ? 'inline-block' : 'hidden'} />
            持っていない
          </button>
        </div>
      </div>
    </>
  );
};

これらのコードが想定している動きとしては以下になります。

終わりに

今回は、『react-hook-form』を用いて自作トグルボタンのデザイン適応を行いました。二つのボタンを一つのボタンとして捉えることができればそんなに難しい内容ではなかったです。今回は、選択肢を明示する必要があったため回りくどいデザインをしましたが、次回はトグルボタンのデザインにチャレンジしていこうと思います。

『react-hook-form』の記事に関してはシリーズものを想定しています。これからも書いていく予定ですのでよろしくお願いします。というか最近Version7が出てました。また勉強します。前回の記事はこちらから

最近投稿頻度が落ちていることが悩みですが、発信は続けていきたいのでTwitterもよろしくお願いします。直近だと何も投稿してないですけど…

ではまた~

 

アバター画像
About 龍:Ryu 107 Articles
2022年入社で主にフロントエンドの業務でTailwindと遊ぶ日々。お酒とうまいご飯が好きで、運動がちょっと嫌いなエンジニアです。しゃべれるエンジニアを目指しておしゃべりとブログ執筆に注力中(業務もね)//
ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。


ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!

Be the first to comment

Leave a Reply

Your email address will not be published.


*


質問はこちら 閉じる