Reactでフォームを作る【React Hook Form】

ReactにおけるフォーマネジメントはHookの登場により大幅に改善されました。特に「React Hook Form」はパフォーマンスと使いやすさを両立したライブラリとして人気です。この記事では、React Hook Formの基本から実践的な使い方までを解説します。

React Hook Formとは

React Hook Formは、フォーム検証のためのシンプルで効率的なライブラリです。以下の特徴があります:

  • 少ないレンダリング回数
  • 不要な再レンダリングの防止
  • 型安全なフォーム処理(TypeScript対応)
  • シンプルなAPI
  • 軽量な実装

インストール方法

npmまたはyarnを使ってインストールできます:

npm install react-hook-form
# or
yarn add react-hook-form

基本的な使い方

React Hook Formの基本的な使い方を見てみましょう:

import React from 'react';
import { useForm } from 'react-hook-form';

function SimpleForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();
  
  const onSubmit = data => {
    console.log(data);
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>名前</label>
        <input {...register("name", { required: "名前は必須です" })} />
        {errors.name && <p>{errors.name.message}</p>}
      </div>
      
      <div>
        <label>メールアドレス</label>
        <input {...register("email", { 
          required: "メールアドレスは必須です",
          pattern: {
            value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
            message: "無効なメールアドレスです"
          }
        })} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>
      
      <button type="submit">送信</button>
    </form>
  );
}

フォームバリデーション

React Hook Formには、以下のような様々なバリデーションルールが用意されています:

  • required: 必須項目
  • min/max: 数値の最小値/最大値
  • minLength/maxLength: 文字列の最小/最大長
  • pattern: 正規表現によるバリデーション
  • validate: カスタムバリデーション関数

カスタムバリデーション

独自のバリデーションルールを定義することも可能です:

<input
  {...register("username", {
    validate: value => {
      return value !== "admin" || "このユーザー名は使用できません";
    }
  })}
/>

フォームのリセット

フォームをリセットするには、reset関数を使います:

const { register, handleSubmit, reset } = useForm();

const onSubmit = data => {
  console.log(data);
  reset(); // フォームをリセット
};

React Hook FormとTypeScript

TypeScriptと組み合わせることで、より安全なフォーム処理が可能になります:

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

type FormInputs = {
  name: string;
  email: string;
  age: number;
};

function TypedForm() {
  const { register, handleSubmit } = useForm<FormInputs>();
  
  const onSubmit: SubmitHandler<FormInputs> = data => {
    console.log(data);
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* フォーム要素 */}
    </form>
  );
}

複雑なフォーム例

より実践的な例として、会員登録フォームを作成してみましょう:

import React from 'react';
import { useForm } from 'react-hook-form';

const RegistrationForm = () => {
  const { register, handleSubmit, watch, formState: { errors }, reset } = useForm();
  const password = watch("password");
  
  const onSubmit = (data) => {
    console.log(data);
    // ここでAPIリクエストを送信するなどの処理
    alert('登録が完了しました!');
    reset();
  };
  
  return (
    <div className="form-container">
      <h2>会員登録</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="form-group">
          <label>ユーザー名</label>
          <input 
            {...register("username", { 
              required: "ユーザー名は必須です", 
              minLength: { value: 4, message: "ユーザー名は4文字以上で入力してください" } 
            })} 
          />
          {errors.username && <p className="error-message">{errors.username.message}</p>}
        </div>
        
        <div className="form-group">
          <label>メールアドレス</label>
          <input 
            {...register("email", { 
              required: "メールアドレスは必須です", 
              pattern: { 
                value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, 
                message: "無効なメールアドレスです" 
              } 
            })} 
          />
          {errors.email && <p className="error-message">{errors.email.message}</p>}
        </div>
        
        <div className="form-group">
          <label>パスワード</label>
          <input 
            type="password" 
            {...register("password", { 
              required: "パスワードは必須です", 
              minLength: { value: 8, message: "パスワードは8文字以上で入力してください" },
              pattern: {
                value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/,
                message: "パスワードは少なくとも1つの大文字、小文字、数字を含む必要があります"
              }
            })} 
          />
          {errors.password && <p className="error-message">{errors.password.message}</p>}
        </div>
        
        <div className="form-group">
          <label>パスワード(確認)</label>
          <input 
            type="password" 
            {...register("confirmPassword", { 
              required: "パスワード(確認)は必須です", 
              validate: value => value === password || "パスワードが一致しません" 
            })} 
          />
          {errors.confirmPassword && <p className="error-message">{errors.confirmPassword.message}</p>}
        </div>
        
        <div className="form-group">
          <label>年齢</label>
          <input 
            type="number" 
            {...register("age", { 
              required: "年齢は必須です", 
              min: { value: 18, message: "18歳以上である必要があります" },
              max: { value: 120, message: "有効な年齢を入力してください" }
            })} 
          />
          {errors.age && <p className="error-message">{errors.age.message}</p>}
        </div>
        
        <div className="form-group checkbox">
          <input 
            type="checkbox" 
            id="terms" 
            {...register("terms", { required: "利用規約に同意する必要があります" })} 
          />
          <label htmlFor="terms">利用規約に同意します</label>
          {errors.terms && <p className="error-message">{errors.terms.message}</p>}
        </div>
        
        <button type="submit" className="submit-button">登録</button>
      </form>
    </div>
  );
};

export default RegistrationForm;

スタイリング例

上記のフォームにCSSを適用する例です:

.form-container {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
  background-color: #f8f9fa;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

h2 {
  text-align: center;
  margin-bottom: 20px;
  color: #333;
}

.form-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: 600;
  color: #555;
}

input {
  width: 100%;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

input:focus {
  outline: none;
  border-color: #4a90e2;
  box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}

.error-message {
  color: #e74c3c;
  font-size: 14px;
  margin-top: 5px;
}

.checkbox {
  display: flex;
  align-items: center;
}

.checkbox input {
  width: auto;
  margin-right: 10px;
}

.checkbox label {
  margin-bottom: 0;
}

.submit-button {
  width: 100%;
  padding: 12px;
  background-color: #4a90e2;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  transition: background-color 0.3s;
}

.submit-button:hover {
  background-color: #3a7bc8;
}

React Hook Formの高度な機能

フォームの状態監視

watch関数を使用して、フォームの値をリアルタイムで監視できます:

const { watch } = useForm();
const watchedValue = watch("fieldName");
// または全てのフィールドを監視
const allValues = watch();

条件付きフィールド

特定の条件に基づいてフィールドを表示/非表示にすることも可能です:

const { register, watch } = useForm();
const showExtraField = watch("enableExtra");

return (
  <form>
    <div>
      <input type="checkbox" {...register("enableExtra")} />
      <label>追加情報を表示</label>
    </div>
    
    {showExtraField && (
      <div>
        <label>追加情報</label>
        <input {...register("extraInfo")} />
      </div>
    )}
  </form>
);

フォームの初期値設定

初期値を設定するには、useFormdefaultValuesを渡します:

const { register } = useForm({
  defaultValues: {
    name: "山田太郎",
    email: "yamada@example.com"
  }
});

まとめ

React Hook Formは、少ないコードで効率的なフォーム処理を実現できるライブラリです。本記事では基本的な使い方から応用例まで紹介しましたが、公式ドキュメントにはさらに多くの機能が紹介されています。

フォーム処理は多くのWebアプリケーションで必須の機能ですが、React Hook Formを活用することで、バリデーションやエラー処理など煩雑になりがちな処理をシンプルに記述できます。ぜひ実際のプロジェクトで活用してみてください。

Next.jsとGatsbyの違い!どっちを選ぶべき?

Webサイトやアプリケーションを開発する際、フレームワーク選びは重要な決断です。特にReactベースのフレームワークとして人気の高いNext.jsとGatsbyは、どちらも優れた機能を提供していますが、それぞれ異なる特徴と用途があります。この記事では、両者の違いを詳しく解説し、プロジェクトに最適な選択肢を見つけるお手伝いをします。

Next.jsとGatsbyの基本

Next.jsとは?

Next.jsはVercelが開発するReactフレームワークで、サーバーサイドレンダリング(SSR)とスタティックサイト生成(SSG)の両方をサポートしています。多様なレンダリング方法と柔軟なルーティングが特徴で、大規模なアプリケーションや動的コンテンツを扱うプロジェクトに適しています。

Gatsbyとは?

GatsbyはReactベースの静的サイトジェネレーターで、GraphQLを利用したデータ取得に特化しています。事前にすべてのページを生成するため、非常に高速なサイトを構築できます。ブログやポートフォリオなど、内容の更新頻度が比較的低いウェブサイトに向いています。

主な違いを比較

1. レンダリング方式

Next.js:

  • サーバーサイドレンダリング(SSR)
  • スタティックサイト生成(SSG)
  • クライアントサイドレンダリング(CSR)
  • インクリメンタル静的再生成(ISR)

Next.jsは複数のレンダリング方式を柔軟に組み合わせられるため、ページごとに最適な方法を選択できます。例えば、ユーザーダッシュボードはSSRで、マーケティングページはSSGで実装するといった使い分けが可能です。

Gatsby:

  • 主に静的サイト生成(SSG)に特化
  • 一部の機能でサーバーサイド処理も可能(Gatsby Functions)

Gatsbyはビルド時にすべてのページを生成するため、非常に高速な読み込みと優れたSEO効果が期待できます。ただし、頻繁に更新される動的コンテンツには不向きな場合があります。

2. データ取得

Next.js:

  • 様々なデータ取得方法を柔軟にサポート
  • RESTful API、GraphQL、その他のデータソースに対応
  • getServerSidePropsgetStaticPropsgetStaticPathsなどの関数

Gatsby:

  • GraphQLによるデータ取得に特化
  • プラグインシステムで様々なデータソースと連携可能
  • ビルド時にデータを取得・処理

Gatsbyは豊富なプラグインエコシステムを持ち、WordPressやContentfulなどのCMSとの連携が簡単です。一方、Next.jsはより自由な方法でデータを取得できるため、複雑なバックエンドとの連携に適しています。

3. パフォーマンスとSEO

Next.js:

  • レンダリング方式によって異なるパフォーマンス特性
  • SSGモードでは非常に高速
  • ISRによる最適なバランス

Gatsby:

  • ビルド時に最適化された高速なサイト
  • 自動的な画像最適化
  • プログレッシブWebアプリ(PWA)の機能

どちらもSEOに強いフレームワークですが、Gatsbyは静的コンテンツの最適化に優れ、Next.jsは動的コンテンツと静的コンテンツのバランスが取れています。

4. 開発体験

Next.js:

  • シンプルで直感的なAPI
  • ファイルベースのルーティング
  • 高速な開発サーバー
  • TypeScriptのサポートが充実

Gatsby:

  • 豊富なプラグイン
  • 強力なテーマシステム
  • GraphQLの学習コストがある
  • ビルド時間が長くなる傾向

Next.jsは設定が少なく、直感的に使える点が魅力です。一方、Gatsbyはプラグインによって機能を拡張しやすいですが、GraphQLの知識が必要です。

5. ユースケース

Next.jsが向いているプロジェクト:

  • 動的コンテンツが多いWebアプリケーション
  • eコマースサイト
  • ユーザー認証が必要なサービス
  • 大規模で複雑なプロジェクト
  • APIと連携するWebアプリケーション

Gatsbyが向いているプロジェクト:

  • ブログやポートフォリオ
  • マーケティングサイト
  • ドキュメントサイト
  • コンテンツ重視のWebサイト
  • CMSと連携するサイト

どちらを選ぶべき?意思決定のポイント

以下のポイントを考慮して、プロジェクトに最適なフレームワークを選びましょう:

  1. コンテンツの更新頻度: 頻繁に更新される動的コンテンツが多い場合はNext.js、静的なコンテンツが中心ならGatsbyが適しています。
  2. 開発チームのスキルセット: GraphQLに精通しているならGatsbyの方が生産性が高まる可能性があります。シンプルなReactの知識だけで始めたい場合はNext.jsが取っつきやすいでしょう。
  3. スケーラビリティ: 将来的に機能拡張や規模の拡大が予想される場合、Next.jsの柔軟性が有利です。
  4. ホスティング環境: Vercel上でのデプロイを考えている場合、Next.jsとの親和性が高いです。一方、GatsbyはNetlifyなどの静的ホスティングサービスと相性が良いです。
  5. ビルド時間: 大量のページを持つサイトでは、Gatsbyのビルド時間が長くなる傾向があります。この点はNext.jsのISRが解決策になることも。

まとめ

Next.jsとGatsbyはどちらも優れたReactフレームワークですが、プロジェクトの要件によって最適な選択肢は異なります。

  • Next.js: 様々なレンダリング方式をサポートし、動的コンテンツと静的コンテンツの両方に対応できる柔軟なフレームワーク。幅広いユースケースに対応可能。
  • Gatsby: GraphQLを活用した静的サイト生成に特化し、高速なパフォーマンスを実現。コンテンツ中心のサイトに最適。

どちらを選ぶにしても、プロジェクトの現在のニーズだけでなく、将来的な発展も考慮に入れて決断することが大切です。また、小規模なプロジェクトでまずは試してみて、実際の開発体験を確かめるのも良い方法です。

最終的には、あなたのチームが心地よく使えるフレームワークが最良の選択です。幸いなことに、どちらも活発に開発が続けられている素晴らしいフレームワークなので、選択を誤ることはないでしょう。

コメント

タイトルとURLをコピーしました