ReactでFormを扱う方法|コードを見ながら手を動かして学ぼう

※本サイトにはプロモーション・広告が含まれています。

(最終更新日:2023年11月)

✔このような方へ向けて書かれた記事となります

「React Formってなにができるの?」
「React Formの作成方法を知りたい」
「React Formの作成例を見て理解したい」

✔当記事を通じて伝えること

  • React Formの基本概念
  • React Formの作成手法とその応用方法
  • React Formの実際の作成例

当記事では、React Formの基本的な理解から、その効果的な利用方法まで、実際のコードを示しながら詳細に解説します。

ぜひ最後までお読みください。

筆者プロフィール

筆者プロフィールアイコン

【現職】プロダクトマネージャー

【副業】ブログ(月間17万PV)/YouTube/Web・アプリ制作

「プログラミング × ライティング × 営業」の経験を活かし、30後半からのIT系職へシフト。当サイトでは、実際に手を動かせるWebアプリの開発を通じて、プログラミングはもちろん、IT職に必要な情報を提供していきます。

【当ブログで紹介しているサイト】

当サイトチュートリアルで作成したデモ版日報アプリ

Django × Reactで開発したツール系Webアプリ

✔人に見せても恥ずかしくないコードを書こう

「リーダブルコード」は、わかりやすく良いコードの定義を教えてくれる本です。

  • 見るからにきれいなコードの書き方
  • コードの分割方法
  • 変数や関数の命名規則

エンジニアのスタンダートとすべき基準を一から解説しています。

何回も読むのに値する本なので、ぜひ手にとって読んでみてください。

React Formとその種類

次に、Reactの中でも特に重要な部分である「Form」について説明します。

Formはユーザーの入力を扱うための重要なツールです。

  • React Formとは何か
  • 制御コンポーネントと非制御コンポーネント
  • null値の扱い

React Formとは何か

ReactのFormは、通常のHTMLのFormと同じく、ユーザーの入力を受け付ける要素をグループ化するもの。

ReactのFormは状態を持つことが可能で、それによりリアクティブなユーザインターフェースを実現します。

制御コンポーネントと非制御コンポーネント

ReactではFormを以下の2種類に分けます。

  • 制御コンポーネント(Controlled Components)
  • 非制御コンポーネント(Uncontrolled Components)

制御コンポーネントは、フォームの各入力要素の状態をReactコンポーネントが管理します。

非制御コンポーネントは、DOM自体がフォームデータを保持するものです。

null値の扱い

Reactでは、form要素にnullundefinedを設定すると、それを「未制御」にすると解釈します。

Reactがその要素を追跡しなくなり、ユーザーの入力に応じて要素が更新される仕組みです。

この挙動を理解しておくと、制御コンポーネントと非制御コンポーネントの動作の違いをより深く理解できます。

React Formの作成: 制御コンポーネント

ここでは、制御コンポーネントを中心にReact Formの作成方法を見ていきます。

異なる種類のフォーム要素についても触れ、それぞれがどのように扱われるかを理解しましょう。

  • テキストエリア
  • セレクトボックス
  • ファイルアップロード
  • 複数の入力の処理
  • 制御コンポーネントの代替法

テキストエリア

テキストエリアは、ユーザーが複数行のテキストを入力するための要素です。

Reactでは、テキストエリアを制御コンポーネントとして使用する場合、value属性を使用してコンポーネントのstateに直接リンクします。

import React, { useState } from "react";

function TextAreaExample() {
  const [text, setText] = useState("");

  function handleChange(event) {
    setText(event.target.value);
  }

  return (
    <textarea value={text} onChange={handleChange} />
  );
}

export default TextAreaExample;

セレクトボックス

セレクトボックスは、ユーザーが選択肢からひとつを選ぶための要素です。

Reactでは、制御コンポーネントとしてセレクトボックスを使用する場合、value属性を使用して選択肢とコンポーネントのstateをリンクします。

import React, { useState } from "react";

function SelectBoxExample() {
  const [fruit, setFruit] = useState("apple");

  function handleChange(event) {
    setFruit(event.target.value);
  }

  return (
    <select value={fruit} onChange={handleChange}>
      <option value="apple">Apple</option>
      <option value="orange">Orange</option>
      <option value="grape">Grape</option>
    </select>
  );
}

export default SelectBoxExample;

ファイルアップロード

ファイルアップロードは、ユーザーがファイルを送信するための要素です。

Reactでは、ファイルアップロードは非制御コンポーネントとして扱われます。

refを使用して直接ファイルへのアクセスを得られます。

import React, { useRef } from "react";

function FileUploadExample() {
  const fileInput = useRef();

  function handleSubmit(event) {
    event.preventDefault();
    alert(`Selected file - ${fileInput.current.files[0].name}`);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Upload file:
        <input type="file" ref={fileInput} />
      </label>
      <br />
      <button type="submit">Submit</button>
    </form>
  );
}

export default FileUploadExample;

複数の入力の処理

場合によっては、ひとつのハンドラ関数で複数の入力を処理できます。

各要素に名前属性(name)を追加し、ハンドラ内でevent.target.nameで参照可能です。

import React, { useState } from "react";

function MultiInputExample() {
  const [person, setPerson] = useState({ firstName: "", lastName: "" });

  function handleChange(event) {
    setPerson({ ...person, [event.target.name]: event.target.value });
  }

  return (
    <form>
      <label>
        First Name:
        <input type="text" name="firstName" onChange={handleChange} />
      </label>
      <label>
        Last Name:
        <input type="text" name="lastName" onChange={handleChange} />
      </label>
    </form>
  );
}

export default MultiInputExample;

制御コンポーネントの代替法

制御コンポーネントは強力ですが、いくつかのシナリオでは非制御コンポーネントのほうが扱いやすいこともあります。

例えば、以下のようなケースです。

  • フォームにプレフィル(予め入力されている)データが必要な場合
  • stateの更新にパフォーマンスが影響を受ける可能性がある場合

React Formの作成: 非制御コンポーネント

ここでは、非制御コンポーネントを中心にReact Formの作成方法を見ていきましょう。

Hookを利用しない場合、そしてuseStateとuseRefの導入などについて解説します。

  • Hookを利用しない場合
  • useStateとuseRefの導入
  • 入力値の表示と取得

Hookを利用しない場合

Reactの初期バージョンでは、非制御コンポーネントを使用してフォームを作成するためにrefsを使用していました。

refはReact要素に対する参照を作成し、それを変数に格納します。

以下のコードは、非制御コンポーネントを使用したフォームの簡単な例です。

import React from 'react';

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

export default NameForm;

useStateとuseRefの導入

現在、Reactの非制御コンポーネントの制作には、useStateとuseRefがよく使われます

以下のコードは、この二つのHookを使用してフォームを作成した例です。

import React, { useRef } from 'react';

function NameForm() {
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // `current` points to the mounted text input element
    alert('A name was submitted: ' + inputEl.current.value);
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Submit</button>
    </>
  );
}

export default NameForm;

入力値の表示と取得

上記の例では、入力フィールドの値が直接取得され、それがアラートダイアログに表示されます。

実際のDOM要素への直接的なアクセスが可能になり、それを使って入力フィールドの現在の値を取得できるのです。

非制御コンポーネントは、内部状態を持たないため、Reactの再レンダリングサイクルの影響を受けないという利点もあります。

React Hook Formの使用

次に、React Hook Formの使用について詳しく説明します。

  • ライブラリのインストールと設定
  • バリデーション、エラーメッセージ、初期値の設定
  • その他formStateのプロパティ
  • バリデーションのタイミング

ライブラリのインストールと設定

React Hook Formは、以下のようにnpm経由でインストールできます。

npm install react-hook-form

次に、以下のコードスニペットは、React Hook Formを使用した簡単なフォームの例です。

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

function NameForm() {
  const { register, handleSubmit, watch, errors } = useForm();
  const onSubmit = data => console.log(data);

  console.log(watch("example")); // watch input value by passing the name of it

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="example" defaultValue="test" ref={register} />
      <input type="submit" />
    </form>
  );
}

export default NameForm;

バリデーション、エラーメッセージ、初期値の設定

React Hook Formを使用すると、簡単にバリデーションルールを追加できます。

さらに、エラーメッセージと初期値も設定できます。

以下はその一例です。

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

function NameForm() {
  const { register, handleSubmit, errors } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="example" ref={register({ required: 'This field is required' })} />
      {errors.example && <p>{errors.example.message}</p>}
      <input type="submit" />
    </form>
  );
}

export default NameForm;

その他formStateのプロパティ

formStateオブジェクトを通じて、さまざまなフォームの状態情報にアクセスできます。

以下はその例です。

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

function NameForm() {
  const { register, handleSubmit, formState } = useForm();
  const onSubmit = data => console.log(data);

  console.log(formState.dirty); // フィールドが変更されたかどうか

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="example" ref={register} />
      <input type="submit" />
    </form>
  );
}

export default NameForm;

バリデーションのタイミング

React Hook Formでは、バリデーションをおこなうタイミングを選択できます。

以下のような場面や要件に応じて、設定を変えることが可能です。

  • データを入力しながらリアルタイムでバリデーションをおこなう
  • フォームの送信時にバリデーションをおこなう

YupとZod: バリデーションライブラリの利用

次に、YupとZodというバリデーションライブラリの使用方法を詳しく説明します。

これらのライブラリを活用することで、より複雑なバリデーションロジックを簡単に管理できるでしょう。

  • Yupの概要とエラーメッセージのローカライズ
  • Zodの概要とエラーメッセージのローカライズ

Yupの概要とエラーメッセージのローカライズ

YupはJavaScriptのオブジェクトスキーマバリデータで、React Hook Formと組み合わせて使用することが一般的です。

Yupのインストールは以下のコマンドでおこなえます。

npm install yup

そして、以下にReact Hook FormとYupを使用したフォームバリデーションの一例を示します。

import React from 'react';
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(),
  age: yup.number().positive().integer().required(),
});

function NameForm() {
  const { register, handleSubmit, errors } = useForm({
    resolver: yupResolver(schema)
  });
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="name" ref={register} />
      {errors.name && <p>This field is required</p>}
      <input name="age" ref={register} />
      {errors.age && <p>Please enter a positive integer</p>}
      <input type="submit" />
    </form>
  );
}

export default NameForm;

Zodの概要とエラーメッセージのローカライズ

ZodはYupと同様に、React Hook Formで使用するためのバリデーションスキーマを作成できるライブラリです。

Zodのインストールは以下のコマンドで行えます。

npm install zod

以下にReact Hook FormとZodを使用したフォームバリデーションの一例を示します。

import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  name: z.string(),
  age: z.number().int().positive(),
});

function NameForm() {
  const { register, handleSubmit, errors } = useForm({
    resolver: zodResolver(schema)
  });
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input name="name" ref={register} />
      {errors.name && <p>This field is required</p>}
      <input name="age" ref={register} />
      {errors.age && <p>Please enter a positive integer</p>}
      <input type="submit" />
    </form>
  );
}

export default NameForm;

これらのバリデーションライブラリはエラーメッセージのローカライズもサポートしており、多言語対応のアプリケーションでも柔軟に対応することが可能です。

フォームデータの送信とリファクタリング

Reactで作成したフォームからデータの送信方法と、より効率的なコードの書き方、すなわちリファクタリングについて解説します。

  • データの送信方法
  • コードのリファクタリングとパフォーマンス最適化

データの送信方法

データの送信には、通常、非同期通信を用いてサーバーへと情報を送る場合が多いです。

以下に、Reactのコンテキストとフックを組み合わせてデータを送信する簡単な例を示します。

import React, { useState } from 'react';
import axios from 'axios';

function MyForm() {
  const [formData, setFormData] = useState({});

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

  const handleSubmit = async (event) => {
    event.preventDefault();
    try {
      const response = await axios.post('http://example.com/submit', formData);
      console.log('Success:', response);
    } catch (error) {
      console.error('Error:', error);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="username" onChange={handleChange} />
      <input type="submit" value="Submit" />
    </form>
  );
}

コードのリファクタリングとパフォーマンス最適化

Reactのコンポーネントが大規模になると、管理が難しくなりがちです。

こうした場合、コードを分割してリファクタリングすることで、保守性や再利用性を高められます

また、不要なレンダリングを抑制することでパフォーマンスの最適化もおこなうことも可能。

以下に、ReactのuseCallbackフックを使用して、不要なレンダリングを抑制する例を示します。

import React, { useState, useCallback } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => setCount((c) => c + 1), []);

  return (
    <div>
      Count: {count}
      <button onClick={increment}>Increment</button>
    </div>
  );
}

このように、useCallbackを使用することで、increment関数のインスタンスが不要に再生成されることを防ぎ、パフォーマンスの最適化を図ることができます。

まとめ

当記事でお伝えしたことは以下のとおり。

  • Reactでのフォームの作成方法
  • 制御コンポーネントと非制御コンポーネントの違い
  • React Hook Formの使用方法
  • バリデーションライブラリ(YupとZod)の利用方法
  • データの送信やコードのリファクタリング

これらの知識を活用することで、Reactを用いた実践的なフォーム開発が可能になります。

この知識を活用して、素晴らしいウェブアプリケーションを作成してみてください!

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