1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

理解に苦しんだ結果、わかりやすく解説!ReactやNext.jsで使えるreact-hook-formとshadcnのフォーム管理

Last updated at Posted at 2025-04-02

Reactでフォームを管理するためのライブラリとして、react-hook-formは非常に便利です。また、shadcnFormコンポーネントを使うことで、スタイルや構造を簡単に整えることができます。この記事では、react-hook-formcontrolfieldの役割を解説し、さらにshadcnFormコンポーネントを使った実装例を紹介します。

1. shadcnのインストール

最初にshadcnをインストールする必要があります。shadcnは個別のコンポーネントをインストールする形式を取ります。

既存プロジェクトにshadcnをセットアップ

npx shadcn-ui@latest init

これで、shadcnのUIコンポーネントをプロジェクトに組み込む準備が整います。

2. formとinputのインストール

次に、フォームとインプットコンポーネントを使用するために、shadcnのformおよびinputパッケージをインストールします。以下のコマンドを実行してください。

  • フォームコンポーネントをインストール
npx shadcn-ui@latest add form
  • インプットコンポーネントをインストール
npx shadcn-ui@latest add input
  • 必要に応じて他のコンポーネントも追加
npx shadcn-ui@latest add select
npx shadcn-ui@latest add checkbox

これで、formおよびinputコンポーネントをプロジェクト内で使用できるようになります。

3. useFormを使ったフォームの初期化

react-hook-formuseFormフックを使ってフォームを初期化します。この例では、食材情報(namecategoryunit)をフォームで入力できるようにしています。

import { useForm } from "react-hook-form";

interface FoodFormData {
  name: string;
  category: string;
  unit: string;
}

const form = useForm<FoodFormData>({
  defaultValues: {
    name: "",
    category: "fruit",
    unit: "kg",
  },
});

このコードでは、useFormでフォームの初期値(defaultValues)を設定しています。namecategoryunitはそれぞれフォームで入力されるフィールドです。categoryunitにはデフォルト値を設定しています。

4. フォームの送信処理

フォームが送信されたときに実行する関数を定義します。具体的には、フォーム送信後にバリデーションが行われ、その結果が正しい場合に実行される関数です。この関数は、フォームデータをサーバーに送信したり、データベースに登録または更新したりするために使用されます。実際のアプリケーションでは、この部分でAPIリクエストを送信し、データをサーバーまたはデータベースに保存する処理を行います。

handleFormSubmitという関数名は任意であり、お好きな名前を付けることができます。

以下は、handleFormSubmit関数の簡単な例です。この関数内でデータ送信処理が行われますが、実際のリクエストは省略しています。

const handleFormSubmit = async (data: FoodFormData) => {
  try {
    // サーバーにデータを送信する(例: POSTリクエストでデータベースに登録)
    // ここに実際のAPIリクエストやデータ保存処理を追加します
  } catch (error) {
    console.error("Error submitting form:", error);
  }
};

5. フォームフィールドの作成

shadcnのコンポーネントを使って、フォームフィールドを作成します。ここでは、FormFieldFormControlFormLabelFormMessageなどのコンポーネントを使い、react-hook-formControllerと連携させています。

import { Form, FormControl, FormField, FormLabel, FormMessage, FormItem } from "@/components/ui/form";
import { Input } from "@/components/ui/input";

<Form {...form}>
  <form onSubmit={form.handleSubmit(handleFormSubmit)}>
    <FormField
      control={form.control}
      name="name"
      rules={{ required: "Food name is required." }}
      render={({ field }) => (
        <FormItem>
          <FormLabel>Food Name</FormLabel>
          <FormControl>
            <Input {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  </form>
</Form>

コード解説

import { Form, FormControl, FormField, FormLabel, FormMessage, FormItem } from "@/components/ui/form";
import { Input } from "@/components/ui/input";

import: shadcnのコンポーネント(Form, FormControl, FormField, FormLabel, FormMessage, FormItem, Input)をインポートしています。これらのコンポーネントは先ほどshadcnでインストールしたものです。フォーム作成を簡素化し、スタイルを整えるために使用します。

<Form {...form}>

<Form {...form}>: react-hook-formのuseFormフックから提供されるformオブジェクトをFormコンポーネントに渡しています。これにより、フォームの状態管理がreact-hook-formで行われ、入力内容やバリデーションなどが制御されます。

  <form onSubmit={form.handleSubmit(handleFormSubmit)}>

<form onSubmit={form.handleSubmit(handleFormSubmit)}>: 実際のHTMLフォームタグです。form.handleSubmit()は、useForm()で作成されたformオブジェクトのプロパティのひとつで、フォームが送信されると、この関数によりバリデーションチェックが行われます。その後、バリデーションが成功すると、引数で渡された関数(この場合はhandleFormSubmit)が実行されます。

<FormField
    control={form.control}
    name="name"
    rules={{ required: "Food name is required." }}
    render={({ field }) => (
      <FormItem>
        <FormLabel>Food Name</FormLabel>
            <FormControl>
                <Input {...field} />
            </FormControl>
            <FormMessage />
      </FormItem>
    )}
  • <FormField>:shadcnのFormFieldコンポーネントは、フォームフィールド全体をラップします。
  • control={form.control}で、react-hook-formのフォーム状態と連携しています。
  • name="name"で、このフィールドの名前(name)を指定しています。
  • rules={{ required: "Food name is required." }}で、このフィールドが必須であることを指定しています。
  • renderは、react-hook-formControllerコンポーネントとshadcnFormFieldコンポーネントをつなぐ役割を持っています。
    renderの引数には、fieldオブジェクトが自動で渡され、このfieldオブジェクトを使って実際の入力フィールドをレンダリングします。
    ここには実際に画面に表示するUI(入力フィールドやラベルなど)を記述します。fieldオブジェクトには、value, onChange, onBlur, refなど、フォームフィールドに必要な情報が含まれています。
controlとfieldの関係

少し詳しく説明すると、controlfieldは、react-hook-formでフォームの状態を管理するために密接に連携しています。

  • controlの役割
    controlは、フォーム全体の状態を管理します。例えば、フォームに3つのフィールド(name, email, age)がある場合、controlはこれらのフィールドの状態を一元管理しています。具体的には、controlには以下のような情報が含まれます:

    • フィールド名(name, email, age)

    • それぞれのフィールドの値(value)

    • バリデーションエラーなどの状態(もしあれば)

    • onChange, onBlurなどのイベントハンドラ

    以下は、controlの一例です:

control: {
  name: { value: 'John', onChange: [Function], onBlur: [Function], ref: <input ref> },
  email: { value: 'example@example.com', onChange: [Function], onBlur: [Function], ref: <input ref> },
  age: { value: 25, onChange: [Function], onBlur: [Function], ref: <input ref> }
}

このcontrolは、フォーム全体の状態を一元的に管理し、fieldに必要なデータを提供します。

  • fieldの役割
    fieldは、Controller内で、個々のフォームフィールドに関連する情報を提供します。具体的には、fieldはcontrolから渡されたフィールド固有の情報を持ち、それを使ってフォームフィールドをレンダリングします。例えば、fieldには以下のような情報が含まれます:

    • value(フィールドの現在の値)

    • onChange(入力が変更されたときに呼ばれる関数)

    • onBlur(フィールドがフォーカスを失ったときに呼ばれる関数)

    • ref(フィールドのDOM参照)

    以下は、fieldの一例です:

field: {
  value: 'John',  // フィールドの値(ここでは名前)
  onChange: [Function],  // フィールドの値が変更されたときに呼ばれる関数
  onBlur: [Function],  // フィールドのフォーカスが外れたときに呼ばれる関数
  ref: <input ref>  // フィールドのDOM参照
}

このfieldオブジェクトが<Input {...field} />に展開されることで、react-hook-formがフォームフィールドの状態を管理します。fieldは、valueやonChangeなどの情報をInputコンポーネントに渡すことで、ユーザーが入力したデータを管理し、フォーム状態をリアルタイムで更新します。

つまり、、

control: フォーム全体の状態を管理します。フォームのすべてのフィールド(name, email, ageなど)の値、バリデーションエラー、イベントハンドラなどを一元的に管理します。

field: controlから渡されたフィールド固有の状態(値、イベントハンドラなど)を持ち、それを使って実際のフィールド(入力フィールド)をレンダリング・操作します。

コードの解説に戻ります。

<FormItem>

<FormItem>: フォームフィールドの個別のラッパーコンポーネントです。これにより、フィールドをさらにカスタマイズしたり、スタイルを適用したりできます。

<FormLabel>Food Name</FormLabel>

<FormLabel>: フィールドのラベル(ここでは「Food Name」)を表示します。このラベルは、フィールドの内容をユーザーに説明するために必要です。

<FormControl>
    <Input {...field} />
</FormControl>

<FormControl>: Inputコンポーネントをラップし、react-hook-formと連携させるためのコンポーネントです。は、fieldオブジェクト(react-hook-formのuseFormから提供されるname, value, onChange, onBlurなど)をInputコンポーネントに渡して、フォームの値と入力要素がバインディングされるようにしています。

<FormMessage />

<FormMessage>: フォームのバリデーションエラーメッセージを表示するためのコンポーネントです。rulesで指定されたバリデーションルールに従って、エラーがあればそのメッセージが表示されます。

「React Hook FormとshadcnUIを組み合わせることで、メンテナンス性が高く、見た目も洗練されたフォームを簡単に実装できます。controlとfieldの関係性を理解することで、より複雑なフォームも自在に操れるようになります。ぜひこの記事を参考に、魅力的なユーザー体験を提供するフォームを作ってみてください!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?