Reactで複数ステップのウィザード形式フォームを簡単に作成する方法

Reactで複数ステップのウィザード形式フォームを作成する方法について、本記事では初心者にも分かりやすく解説します。ウィザード形式フォームは、大量の入力フィールドを複数のステップに分割して表示することで、ユーザーエクスペリエンスを向上させる重要な手法です。Reactの強力なコンポーネント構造と状態管理機能を活用することで、柔軟で使いやすいフォームを効率的に構築できます。この記事を通じて、ウィザード形式フォームをゼロから構築し、実践で応用できるスキルを身につけましょう。

目次

ウィザード形式フォームとは


ウィザード形式フォームは、複数のステップに分割された入力フォームのことを指します。一般的に、次の特徴があります。

ステップごとに分割された入力


フォームがステップごとに分かれているため、ユーザーは一度に入力する情報量を減らせます。これにより、直感的な操作が可能となり、フォームの途中で離脱する可能性を低減します。

進行状況の視覚化


ウィザード形式フォームは進行状況を視覚的に示すことが多く、ユーザーは自分がどの段階にいるかを簡単に把握できます。プログレスバーやステップインジケーターがその一例です。

メリット

  • ユーザビリティの向上: ユーザーが一度に扱う情報量が少なくなり、ストレスが軽減されます。
  • エラーの低減: ステップごとに入力内容を確認できるため、エラーを減らすことができます。
  • 応用の柔軟性: 様々な用途に応用でき、ECサイトのチェックアウトやアカウント登録などで利用されます。

ウィザード形式フォームは、単なるデザインではなく、ユーザー体験を向上させるための重要なツールです。Reactを使用してこれを効果的に実現する方法を、この記事では詳細に解説していきます。

Reactでのフォーム設計の基本

Reactでフォームを設計する際には、効率的でメンテナンス性の高いアプローチを取ることが重要です。以下に、Reactでのフォーム構築の基本概念を紹介します。

コンポーネントベースの設計


Reactでは、フォーム全体を複数の小さなコンポーネントに分割することで、再利用性と可読性を向上させます。例えば、各ステップを個別のコンポーネントとして設計することで、独立性を保ちながら作成が可能です。

状態管理


フォームデータの状態はReactのuseStateuseReducerを利用して管理します。フォーム全体のデータを一元管理することで、ステップ間のデータ共有や検証を簡単に行うことができます。

const [formData, setFormData] = useState({
  step1: { name: '', email: '' },
  step2: { address: '', phone: '' },
});

バリデーションの統合


入力内容をリアルタイムで検証する仕組みを組み込むことで、ユーザーの入力エラーを未然に防ぎます。React Hook FormやYupライブラリを使用すると、検証ロジックを簡潔に記述できます。

イベントハンドリング


フォームの操作を効率的に処理するために、Reactのイベントハンドリングを適切に設定します。例えば、入力フィールドのonChangeイベントを使ってデータを動的に更新します。

const handleChange = (step, field, value) => {
  setFormData((prevData) => ({
    ...prevData,
    [step]: {
      ...prevData[step],
      [field]: value,
    },
  }));
};

コンテナコンポーネントの利用


フォーム全体の状態管理やイベント処理を統括するコンテナコンポーネントを設けることで、個々のフォームステップコンポーネントをシンプルに保てます。

これらの基本を理解し、効率的なReactフォームの設計を進めることで、後続のステップの開発がスムーズになります。次のセクションでは、ウィザード形式フォームの具体的な構築手順に進みます。

必要なツールとライブラリの準備

ウィザード形式フォームをReactで作成するためには、いくつかのツールやライブラリが便利です。これらを準備することで、フォームの構築が効率的かつスムーズになります。

React


React自体はもちろんのこと、最新バージョンを利用することで機能の最適化が図れます。以下はReactプロジェクトのセットアップ手順です。

npx create-react-app wizard-form
cd wizard-form
npm start

状態管理ライブラリ


フォームデータの状態を効率的に管理するために、次のライブラリを活用できます。

  • React Hook Form: 簡単にフォーム状態を管理し、入力検証を組み込めるライブラリ。
  npm install react-hook-form
  • Redux(複雑なプロジェクト向け): グローバル状態管理が必要な場合に活用。

バリデーションライブラリ


フォーム入力の検証を簡単に実装するために次のツールを利用します。

  • Yup: 直感的なスキーマベースのバリデーションが可能。
  npm install yup
  • Zod: 型定義とバリデーションを組み合わせたライブラリ。

UIコンポーネントライブラリ


視覚的に魅力的なフォームを作成するために、以下のUIライブラリを使用できます。

  • Material-UI(MUI): React専用の豊富なUIコンポーネントが含まれています。
  npm install @mui/material @emotion/react @emotion/styled
  • Ant Design: 高品質なコンポーネントライブラリ。

フォームナビゲーションのライブラリ


ウィザード形式フォームでは、ステップ間のナビゲーションが重要です。React Routerを活用することで、ステップごとのページ遷移も簡単に管理できます。

npm install react-router-dom

プロジェクトセットアップ例


上記のツールをインストール後、以下の構造でフォルダを整理すると効率的です。

/src
  /components
    Step1.js
    Step2.js
    Step3.js
  /utils
    validationSchema.js
  App.js
  index.js

これらのツールとライブラリを準備することで、ウィザード形式フォームの開発を効率化できます。次のセクションでは、ステップごとの状態管理ロジックについて詳しく解説します。

ステップ管理のロジック構築

ウィザード形式フォームを実現するためには、各ステップの進行状況を管理するロジックを作成することが重要です。このセクションでは、Reactを使ってステップごとの状態を効率的に管理する方法を解説します。

ステップの状態を管理する


ReactのuseStateフックを使用して、現在のステップを追跡します。以下は基本的な構造です。

import { useState } from "react";

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);

  const nextStep = () => {
    setCurrentStep((prevStep) => prevStep + 1);
  };

  const prevStep = () => {
    setCurrentStep((prevStep) => prevStep - 1);
  };

  return (
    <div>
      <h1>Step {currentStep}</h1>
      <button onClick={prevStep} disabled={currentStep === 1}>
        Back
      </button>
      <button onClick={nextStep}>
        Next
      </button>
    </div>
  );
}

export default WizardForm;

ステップごとのコンポーネントの切り替え


各ステップを別々のコンポーネントとして設計し、現在のステップに応じて適切なコンポーネントをレンダリングします。

function Step1() {
  return <div>Step 1: Enter your name</div>;
}

function Step2() {
  return <div>Step 2: Enter your email</div>;
}

function Step3() {
  return <div>Step 3: Confirm your details</div>;
}

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);

  const renderStep = () => {
    switch (currentStep) {
      case 1:
        return <Step1 />;
      case 2:
        return <Step2 />;
      case 3:
        return <Step3 />;
      default:
        return <Step1 />;
    }
  };

  return (
    <div>
      {renderStep()}
      <button onClick={() => setCurrentStep(currentStep - 1)} disabled={currentStep === 1}>
        Back
      </button>
      <button onClick={() => setCurrentStep(currentStep + 1)}>
        Next
      </button>
    </div>
  );
}

ステップ制御の改善


次のような関数を追加して、ステップがフォームの範囲外に進むのを防ぎます。

const nextStep = () => {
  setCurrentStep((prevStep) => Math.min(prevStep + 1, 3));
};

const prevStep = () => {
  setCurrentStep((prevStep) => Math.max(prevStep - 1, 1));
};

進行状況の視覚化


進行状況を視覚的に表示するために、プログレスバーを追加するのも有効です。以下は簡単な例です。

function ProgressBar({ currentStep, totalSteps }) {
  const percentage = (currentStep / totalSteps) * 100;

  return (
    <div style={{ width: "100%", backgroundColor: "#ccc" }}>
      <div
        style={{
          width: `${percentage}%`,
          height: "10px",
          backgroundColor: "green",
        }}
      />
    </div>
  );
}

これらのロジックを活用すれば、ウィザード形式フォームのステップ管理を効率的に行うことができます。次のセクションでは、各ステップをコンポーネントとして設計する具体的な方法について説明します。

フォームステップのコンポーネント化

ウィザード形式フォームを効率的に構築するためには、各ステップを独立したコンポーネントとして設計することが重要です。このセクションでは、各ステップをコンポーネント化する方法とその利点について解説します。

ステップの役割ごとの分割


フォームの各ステップには特定の役割があり、それぞれが異なる入力フィールドや機能を持ちます。この役割に応じて、コンポーネントを分割します。

例として、次のようなフォームを考えます:

  1. ユーザー情報入力
  2. アドレス入力
  3. 内容確認

コンポーネントの作成


それぞれのステップを独立したコンポーネントとして作成します。以下に例を示します。

Step1.js

function Step1({ formData, handleChange }) {
  return (
    <div>
      <h2>Step 1: User Information</h2>
      <label>
        Name:
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
      </label>
      <br />
      <label>
        Email:
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
      </label>
    </div>
  );
}

export default Step1;

Step2.js

function Step2({ formData, handleChange }) {
  return (
    <div>
      <h2>Step 2: Address Information</h2>
      <label>
        Address:
        <input
          type="text"
          name="address"
          value={formData.address}
          onChange={handleChange}
        />
      </label>
      <br />
      <label>
        Phone:
        <input
          type="tel"
          name="phone"
          value={formData.phone}
          onChange={handleChange}
        />
      </label>
    </div>
  );
}

export default Step2;

Step3.js

function Step3({ formData }) {
  return (
    <div>
      <h2>Step 3: Confirm Your Details</h2>
      <p>Name: {formData.name}</p>
      <p>Email: {formData.email}</p>
      <p>Address: {formData.address}</p>
      <p>Phone: {formData.phone}</p>
    </div>
  );
}

export default Step3;

フォームの統合


これらのコンポーネントを統合して、ウィザード形式フォーム全体を構築します。

import { useState } from "react";
import Step1 from "./Step1";
import Step2 from "./Step2";
import Step3 from "./Step3";

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    address: "",
    phone: "",
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  const nextStep = () => setCurrentStep((prev) => Math.min(prev + 1, 3));
  const prevStep = () => setCurrentStep((prev) => Math.max(prev - 1, 1));

  const renderStep = () => {
    switch (currentStep) {
      case 1:
        return <Step1 formData={formData} handleChange={handleChange} />;
      case 2:
        return <Step2 formData={formData} handleChange={handleChange} />;
      case 3:
        return <Step3 formData={formData} />;
      default:
        return <Step1 formData={formData} handleChange={handleChange} />;
    }
  };

  return (
    <div>
      {renderStep()}
      <button onClick={prevStep} disabled={currentStep === 1}>
        Back
      </button>
      <button onClick={nextStep} disabled={currentStep === 3}>
        Next
      </button>
    </div>
  );
}

export default WizardForm;

コンポーネント化の利点

  1. コードの可読性向上: 各ステップが明確に分かれるため、コードが読みやすくなります。
  2. 再利用性: 各ステップのコンポーネントを他のプロジェクトでも利用可能です。
  3. 保守性: 個々のコンポーネントが独立しているため、バグ修正や機能追加が簡単です。

これでフォームステップを効果的にコンポーネント化できます。次は、フォーム全体の状態管理について解説します。

フォームデータの状態管理

ウィザード形式フォームでは、ステップごとに入力されたデータを適切に管理することが重要です。Reactの状態管理機能を活用し、フォームデータを一元管理する方法を解説します。

フォームデータの一元管理


全ステップのフォームデータを一つの状態オブジェクトに格納し、各ステップで入力されたデータを更新します。これにより、全体のデータを一貫して管理できます。

以下は、フォームデータを状態として管理する基本構造です。

import { useState } from "react";

function WizardForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    address: "",
    phone: "",
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  return <div>Form implementation</div>;
}
export default WizardForm;

ステップごとのデータ更新


handleChange関数を各ステップのコンポーネントに渡すことで、入力フィールドのデータをリアルタイムで更新できます。

function Step1({ formData, handleChange }) {
  return (
    <div>
      <label>
        Name:
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
      </label>
      <br />
      <label>
        Email:
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
      </label>
    </div>
  );
}

状態管理の柔軟性を高める


以下の手法を用いることで、フォームデータの状態管理をさらに効率化できます。

useReducerの活用


フォームデータが複雑になる場合、useReducerを利用して状態管理を簡略化できます。

import { useReducer } from "react";

const initialState = {
  name: "",
  email: "",
  address: "",
  phone: "",
};

function reducer(state, action) {
  switch (action.type) {
    case "UPDATE_FIELD":
      return { ...state, [action.field]: action.value };
    default:
      return state;
  }
}

function WizardForm() {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleChange = (e) => {
    dispatch({
      type: "UPDATE_FIELD",
      field: e.target.name,
      value: e.target.value,
    });
  };

  return <div>Form implementation</div>;
}
export default WizardForm;

Context APIの活用


フォームデータをコンテキストでグローバルに共有することで、深いネスト構造でも簡単にアクセス可能です。

データの送信とリセット


全ステップが完了した際にデータを送信し、フォームをリセットする処理を追加します。

const handleSubmit = () => {
  console.log(formData); // データ送信処理
  setFormData({
    name: "",
    email: "",
    address: "",
    phone: "",
  });
};

統合例


以下はフォーム全体をまとめた例です。

function WizardForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    address: "",
    phone: "",
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  const handleSubmit = () => {
    console.log("Form Submitted", formData);
    setFormData({
      name: "",
      email: "",
      address: "",
      phone: "",
    });
  };

  return (
    <div>
      <h1>Wizard Form</h1>
      {/* ステップのレンダリング */}
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}

この方法を使えば、フォームデータを一元的に管理しながら、柔軟で効率的な開発が可能になります。次は入力検証とエラー処理の方法について説明します。

入力検証とエラー処理

ウィザード形式フォームでは、正しいデータを確保するために入力検証とエラー処理が欠かせません。このセクションでは、Reactを使った入力検証の実装方法とエラーの表示方法について解説します。

基本的な入力検証


入力値を検証するためには、以下のようにuseStateを利用してエラー状態を管理します。

import { useState } from "react";

function Step1({ formData, handleChange, errors, setErrors }) {
  const validate = () => {
    let newErrors = {};
    if (!formData.name.trim()) newErrors.name = "Name is required";
    if (!formData.email.includes("@")) newErrors.email = "Email is invalid";
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleNext = () => {
    if (validate()) {
      console.log("Valid step data");
    }
  };

  return (
    <div>
      <label>
        Name:
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
        {errors.name && <p>{errors.name}</p>}
      </label>
      <br />
      <label>
        Email:
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
        {errors.email && <p>{errors.email}</p>}
      </label>
      <br />
      <button onClick={handleNext}>Next</button>
    </div>
  );
}

export default Step1;

React Hook Formを活用した検証


React Hook Formは、フォーム検証を簡単に実装できるライブラリです。以下の例では、React Hook FormとYupを使ってバリデーションを行います。

ライブラリのインストール

npm install react-hook-form yup @hookform/resolvers

フォームの実装例

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

const schema = yup.object().shape({
  name: yup.string().required("Name is required"),
  email: yup.string().email("Invalid email").required("Email is required"),
});

function Step1() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  });

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        Name:
        <input {...register("name")} />
        {errors.name && <p>{errors.name.message}</p>}
      </label>
      <br />
      <label>
        Email:
        <input {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </label>
      <br />
      <button type="submit">Next</button>
    </form>
  );
}

export default Step1;

リアルタイムバリデーション


入力中にエラーを表示するリアルタイムバリデーションを実装する場合は、onChangeイベントで検証を行います。

const handleChange = (e) => {
  const { name, value } = e.target;
  setFormData((prev) => ({ ...prev, [name]: value }));

  // リアルタイム検証
  if (name === "email" && !value.includes("@")) {
    setErrors((prev) => ({ ...prev, email: "Invalid email format" }));
  } else {
    setErrors((prev) => ({ ...prev, email: null }));
  }
};

エラーの視覚的表示


エラーがある場合、フィールドのデザインを変えると視覚的なわかりやすさが向上します。

<input
  type="text"
  name="name"
  value={formData.name}
  onChange={handleChange}
  style={{ borderColor: errors.name ? "red" : "black" }}
/>

エラー処理の統合例


以下はエラー処理を統合したウィザード形式フォームの例です。

function WizardForm() {
  const [formData, setFormData] = useState({ name: "", email: "" });
  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const validate = () => {
    const newErrors = {};
    if (!formData.name) newErrors.name = "Name is required";
    if (!formData.email.includes("@")) newErrors.email = "Invalid email";
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = () => {
    if (validate()) {
      console.log("Form Submitted", formData);
    }
  };

  return (
    <div>
      <h1>Wizard Form</h1>
      <label>
        Name:
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
        {errors.name && <p>{errors.name}</p>}
      </label>
      <br />
      <label>
        Email:
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
        {errors.email && <p>{errors.email}</p>}
      </label>
      <br />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  );
}
export default WizardForm;

これで、入力検証とエラー処理を適切に組み込むことができました。次は、動的なステップ制御とナビゲーションについて解説します。

動的なステップ制御とナビゲーション

ウィザード形式フォームの動的なステップ制御とナビゲーション機能は、ユーザーがスムーズにフォームを進行・戻ることを可能にします。このセクションでは、Reactで効率的にステップの進行、戻り、条件に基づく制御を実装する方法を解説します。

基本的なステップ制御


ステップ制御には、現在のステップを追跡する状態と、進行・戻る機能を実装します。

import { useState } from "react";

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);

  const nextStep = () => setCurrentStep((prev) => prev + 1);
  const prevStep = () => setCurrentStep((prev) => Math.max(prev - 1, 1));

  return (
    <div>
      <h2>Step {currentStep}</h2>
      <button onClick={prevStep} disabled={currentStep === 1}>
        Back
      </button>
      <button onClick={nextStep}>
        Next
      </button>
    </div>
  );
}
export default WizardForm;

動的なステップ条件


フォーム内容やユーザー入力に基づいて、次のステップに進む条件を設定します。

const nextStep = () => {
  if (formData.name && formData.email.includes("@")) {
    setCurrentStep((prev) => prev + 1);
  } else {
    alert("Please complete the required fields!");
  }
};

任意のステップへのジャンプ


進捗状況を表示するナビゲーションバーを用意し、ユーザーが任意のステップに直接移動できるようにします。

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);

  const jumpToStep = (step) => setCurrentStep(step);

  return (
    <div>
      <div>
        {[1, 2, 3].map((step) => (
          <button
            key={step}
            onClick={() => jumpToStep(step)}
            style={{
              fontWeight: currentStep === step ? "bold" : "normal",
            }}
          >
            Step {step}
          </button>
        ))}
      </div>
      <h2>Step {currentStep}</h2>
    </div>
  );
}
export default WizardForm;

進行状況の視覚化


進行状況を視覚的に示すことで、ユーザーの操作性を向上させます。プログレスバーを追加する方法を例にします。

function ProgressBar({ currentStep, totalSteps }) {
  const percentage = (currentStep / totalSteps) * 100;

  return (
    <div style={{ width: "100%", backgroundColor: "#ccc" }}>
      <div
        style={{
          width: `${percentage}%`,
          height: "10px",
          backgroundColor: "green",
        }}
      />
    </div>
  );
}

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);

  return (
    <div>
      <ProgressBar currentStep={currentStep} totalSteps={3} />
      <h2>Step {currentStep}</h2>
      <button
        onClick={() => setCurrentStep((prev) => Math.max(prev - 1, 1))}
        disabled={currentStep === 1}
      >
        Back
      </button>
      <button
        onClick={() => setCurrentStep((prev) => Math.min(prev + 1, 3))}
        disabled={currentStep === 3}
      >
        Next
      </button>
    </div>
  );
}
export default WizardForm;

エラー時のナビゲーション制限


ユーザーが未入力のフィールドを残したまま次に進めないようにします。

const nextStep = () => {
  if (!formData.name || !formData.email) {
    alert("Please fill out all fields!");
  } else {
    setCurrentStep((prev) => prev + 1);
  }
};

統合例


以下は、動的ステップ制御とナビゲーションを統合した例です。

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);
  const [formData, setFormData] = useState({ name: "", email: "" });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const nextStep = () => {
    if (!formData.name || !formData.email) {
      alert("Please complete all fields!");
    } else {
      setCurrentStep((prev) => Math.min(prev + 1, 3));
    }
  };

  const prevStep = () => setCurrentStep((prev) => Math.max(prev - 1, 1));

  return (
    <div>
      <ProgressBar currentStep={currentStep} totalSteps={3} />
      <div>
        {currentStep === 1 && (
          <div>
            <label>
              Name:
              <input
                type="text"
                name="name"
                value={formData.name}
                onChange={handleChange}
              />
            </label>
          </div>
        )}
        {currentStep === 2 && (
          <div>
            <label>
              Email:
              <input
                type="email"
                name="email"
                value={formData.email}
                onChange={handleChange}
              />
            </label>
          </div>
        )}
        {currentStep === 3 && (
          <div>
            <h3>Confirmation</h3>
            <p>Name: {formData.name}</p>
            <p>Email: {formData.email}</p>
          </div>
        )}
      </div>
      <button onClick={prevStep} disabled={currentStep === 1}>
        Back
      </button>
      <button onClick={nextStep} disabled={currentStep === 3}>
        Next
      </button>
    </div>
  );
}
export default WizardForm;

この実装により、ユーザーはステップをスムーズに進みつつ、必要に応じて戻ることが可能です。次はReact Hook Formを活用した応用例について解説します。

応用例:React Hook Formを活用したフォーム作成

React Hook Formは、フォームの管理を効率化し、簡潔なコードでバリデーションや状態管理を実現する強力なライブラリです。このセクションでは、React Hook Formを活用してウィザード形式フォームを構築する方法を解説します。

React Hook Formの利点

  • 簡潔なコード: 冗長な状態管理やバリデーションコードを省略可能。
  • 高速レンダリング: 状態管理が軽量で、パフォーマンスが向上。
  • 豊富なバリデーションオプション: Yupなどのライブラリとの統合で高度なバリデーションが可能。

React Hook Formのセットアップ


まず、React Hook FormとYupをインストールします。

npm install react-hook-form yup @hookform/resolvers

ステップごとのフォーム作成


以下に、React Hook Formを使ったステップごとのフォーム実装例を示します。

Step1.js

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

function Step1({ onNext, setFormData, formData }) {
  const { register, handleSubmit, formState: { errors } } = useForm({
    defaultValues: { name: formData.name },
  });

  const onSubmit = (data) => {
    setFormData((prev) => ({ ...prev, ...data }));
    onNext();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        Name:
        <input {...register("name", { required: "Name is required" })} />
        {errors.name && <p>{errors.name.message}</p>}
      </label>
      <button type="submit">Next</button>
    </form>
  );
}

export default Step1;

Step2.js

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

function Step2({ onNext, setFormData, formData, onPrev }) {
  const { register, handleSubmit, formState: { errors } } = useForm({
    defaultValues: { email: formData.email },
  });

  const onSubmit = (data) => {
    setFormData((prev) => ({ ...prev, ...data }));
    onNext();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        Email:
        <input
          {...register("email", {
            required: "Email is required",
            pattern: { value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: "Invalid email" }
          })}
        />
        {errors.email && <p>{errors.email.message}</p>}
      </label>
      <button type="button" onClick={onPrev}>Back</button>
      <button type="submit">Next</button>
    </form>
  );
}

export default Step2;

フォーム全体の統合


全体を統括するコンポーネントを作成し、各ステップを動的に切り替えます。

WizardForm.js

import { useState } from "react";
import Step1 from "./Step1";
import Step2 from "./Step2";

function WizardForm() {
  const [currentStep, setCurrentStep] = useState(1);
  const [formData, setFormData] = useState({ name: "", email: "" });

  const nextStep = () => setCurrentStep((prev) => prev + 1);
  const prevStep = () => setCurrentStep((prev) => Math.max(prev - 1, 1));

  const renderStep = () => {
    switch (currentStep) {
      case 1:
        return <Step1 onNext={nextStep} setFormData={setFormData} formData={formData} />;
      case 2:
        return <Step2 onNext={nextStep} onPrev={prevStep} setFormData={setFormData} formData={formData} />;
      default:
        return <div>Thank you for submitting!</div>;
    }
  };

  return (
    <div>
      {renderStep()}
    </div>
  );
}

export default WizardForm;

高度なバリデーションの実装


React Hook FormとYupを組み合わせて高度なバリデーションを実装できます。

Yupスキーマ例

import * as yup from "yup";

const validationSchema = yup.object().shape({
  name: yup.string().required("Name is required"),
  email: yup.string().email("Invalid email").required("Email is required"),
});

Yupを利用したフォーム例

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import validationSchema from "./validationSchema";

function Step1() {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: yupResolver(validationSchema),
  });

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        Name:
        <input {...register("name")} />
        {errors.name && <p>{errors.name.message}</p>}
      </label>
      <label>
        Email:
        <input {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

export default Step1;

これにより、React Hook Formを活用した効率的なウィザード形式フォームを実現できます。次に、記事のまとめを説明します。

まとめ

本記事では、Reactを使用してウィザード形式フォームを作成する方法を解説しました。ウィザード形式フォームの基本構造から、状態管理、動的なナビゲーション、React Hook FormやYupを活用した高度なバリデーションの実装までを網羅しました。

ウィザード形式フォームは、ユーザー体験を向上させる有効な手法であり、適切なツールを活用することで効率的に開発できます。この記事を参考にして、実践的なフォーム作成に挑戦してみてください!

コメント

コメントする

目次