Knowledge Center
技術情報などを公開しています
en ja zh
React入門 全12回シリーズ

カスタムフックで再利用可能なロジックを作成(第11回)

By Author
image
Reactの関数コンポーネントでは、フックを活用することで状態管理や副作用処理を効率的に行えます。しかし、複数のコンポーネントで同じロジックを繰り返し記述することは非効率です。カスタムフックは、この問題を解決し、再利用可能なロジックを簡潔に共有する方法を提供します。本連載の第11回では、カスタムフックの設計と実装をハンズオン形式で学び、コードのモジュール性を高める方法を習得します。第1回で構築したReact開発環境、第2回のJSX、第3回のProps、第4回の`useState`、第5回のイベントハンドリング、第6回のリストレンダリング、第7回の`useEffect`、第8回のフォーム、第9回のReact Router、第10回のコンテキストAPIの知識を基に、macOS環境で開発を進めます。本記事は、プログラミングの基礎知識を持つ読者を対象としています。

カスタムフックの基本概念

カスタムフックは、Reactの組み込みフック(useStateuseEffectなど)を組み合わせて、特定の機能を持つ再利用可能な関数です。カスタムフックは、名前がuseで始まり、コンポーネントや他のフック内で呼び出されます。ロジックをコンポーネントから分離することで、コードの重複を減らし、テストやメンテナンスが容易になります。基本的なカスタムフックの例は以下の通りです。

import { useState } from 'react';    
    
function useCounter() {    
  const [count, setCount] = useState(0);    
    
  const increment = () => setCount(count + 1);    
  const decrement = () => setCount(count - 1);    
    
  return { count, increment, decrement };    
}    

このuseCounterフックは、カウンターの状態と操作を提供し、複数のコンポーネントで再利用できます。

ハンズオン:カスタムフックでフォーム入力を管理

カスタムフックの基本を理解するため、フォーム入力を管理するカスタムフックを作成します。第1回で作成したプロジェクト(my-react-app)を使用します。まず、src/useForm.jsを作成してカスタムフックを定義します。

import { useState } from 'react';    
    
function useForm(initialValues) {    
  const [values, setValues] = useState(initialValues);    
    
  const handleChange = (e) => {    
    const { name, value } = e.target;    
    setValues((prev) => ({    
      ...prev,    
      [name]: value,    
    }));    
  };    
    
  const resetForm = () => {    
    setValues(initialValues);    
  };    
    
  return { values, handleChange, resetForm };    
}    
    
export default useForm;    

このuseFormフックは、フォームの入力値を管理し、変更ハンドリングとリセット機能を提供します。次に、src/App.jsを以下のように編集して、カスタムフックを使用したフォームを実装します。

import React, { useState } from 'react';    
import useForm from './useForm';    
import './App.css';    
    
function App() {    
  const { values, handleChange, resetForm } = useForm({    
    name: '',    
    email: '',    
  });    
  const [message, setMessage] = useState('');    
    
  const handleSubmit = () => {    
    if (values.name.trim() && values.email.trim()) {    
      setMessage(`こんにちは、${values.name}さん!メール: ${values.email}`);    
      resetForm();    
    } else {    
      setMessage('すべてのフィールドを入力してください。');    
    }    
  };    
    
  return (    
    <div className="app-container">    
      <h1>カスタムフックでフォーム管理</h1>    
      <div>    
        <input    
          type="text"    
          name="name"    
          value={values.name}    
          onChange={handleChange}    
          placeholder="名前を入力"    
        />    
        <input    
          type="email"    
          name="email"    
          value={values.email}    
          onChange={handleChange}    
          placeholder="メールアドレスを入力"    
        />    
        <button onClick={handleSubmit}>送信</button>    
        <button onClick={resetForm}>リセット</button>    
      </div>    
      <p>{message}</p>    
    </div>    
  );    
}    
    
export default App;    

このコードでは、useFormフックを使用してフォームの状態とハンドリングを管理します。resetFormでフォームを初期状態に戻し、handleSubmitで入力値を処理します。src/App.cssに以下のスタイルを適用します(第8回と同様)。

.app-container {    
  text-align: center;    
  padding: 20px;    
  font-family: Arial, sans-serif;    
}    
    
input {    
  padding: 8px;    
  margin: 10px;    
  font-size: 16px;    
}    
    
button {    
  padding: 10px 20px;    
  font-size: 16px;    
  cursor: pointer;    
  margin: 5px;    
}    

開発サーバー(npm start)が起動している状態で保存すると、ブラウザにフォームが表示されます。名前とメールアドレスを入力して「送信」をクリックするとメッセージが表示され、「リセット」をクリックするとフォームがクリアされます。この動作は、useFormフックがフォームロジックを効率的に管理していることを示しています。

カスタムフックでデータ取得を管理

カスタムフックは、副作用の管理にも役立ちます。APIデータ取得を管理するカスタムフックを作成します。src/useFetch.jsを作成します。

import { useState, useEffect } from 'react';    
    
function useFetch(url) {    
  const [data, setData] = useState(null);    
  const [loading, setLoading] = useState(true);    
  const [error, setError] = useState(null);    
    
  useEffect(() => {    
    setLoading(true);    
    fetch(url)    
      .then((response) => response.json())    
      .then((data) => {    
        setData(data);    
        setLoading(false);    
      })    
      .catch((err) => {    
        setError(err.message);    
        setLoading(false);    
      });    
  }, [url]);    
    
  return { data, loading, error };    
}    
    
export default useFetch;    

このuseFetchフックは、指定されたURLからデータを取得し、データ、読み込み状態、エラーを返します。App.jsを以下のように変更して、useFetchを使用します。

import React from 'react';    
import useFetch from './useFetch';    
import './App.css';    
    
function App() {    
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');    
    
  return (    
    <div className="app-container">    
      <h1>データ取得のカスタムフック</h1>    
      {loading && <p>読み込み中...</p>}    
      {error && <p>エラー: {error}</p>}    
      {data && (    
        <ul>    
          {data.map((user) => (    
            <li key={user.id}>{user.name}</li>    
          ))}    
        </ul>    
      )}    
    </div>    
  );    
}    
    
export default App;    

App.cssにリストのスタイルを追加します(第7回と同様)。

ul {    
  list-style: none;    
  padding: 0;    
}    
    
li {    
  margin: 10px 0;    
  padding: 10px;    
  background-color: #f0f0f0;    
  border-radius: 5px;    
  max-width: 300px;    
  margin-left: auto;    
  margin-right: auto;    
}    

ブラウザで確認すると、JSONPlaceholderから取得したユーザーリストが表示されます。読み込み中は「読み込み中...」、エラーがあればエラーメッセージが表示されます。useFetchフックにより、データ取得ロジックが再利用可能になっています。

ハンズオン:カスタムフックの拡張

カスタムフックの柔軟性を体感するため、読者自身で機能を追加します。以下の課題を試してください。useForm.jsを編集し、入力値のバリデーションを追加する機能を組み込みます。以下は一例です。

import { useState } from 'react';    
    
function useForm(initialValues, validate) {    
  const [values, setValues] = useState(initialValues);    
  const [errors, setErrors] = useState({});    
    
  const handleChange = (e) => {    
    const { name, value } = e.target;    
    setValues((prev) => ({    
      ...prev,    
      [name]: value,    
    }));    
    if (validate) {    
      const validationErrors = validate({ ...values, [name]: value });    
      setErrors(validationErrors);    
    }    
  };    
    
  const resetForm = () => {    
    setValues(initialValues);    
    setErrors({});    
  };    
    
  return { values, handleChange, resetForm, errors };    
}    
    
export default useForm;    

App.jsを以下のように変更してバリデーションを実装します。

import React, { useState } from 'react';    
import useForm from './useForm';    
import './App.css';    
    
function App() {    
  const validate = (values) => {    
    const errors = {};    
    if (!values.name.trim()) errors.name = '名前は必須です。';    
    if (!values.email.includes('@')) errors.email = '有効なメールアドレスを入力してください。';    
    return errors;    
  };    
    
  const { values, handleChange, resetForm, errors } = useForm(    
    { name: '', email: '' },    
    validate    
  );    
  const [message, setMessage] = useState('');    
    
  const handleSubmit = () => {    
    if (!errors.name && !errors.email && values.name.trim() && values.email.trim()) {    
      setMessage(`こんにちは、${values.name}さん!メール: ${values.email}`);    
      resetForm();    
    } else {    
      setMessage('フォームにエラーがあります。');    
    }    
  };    
    
  return (    
    <div className="app-container">    
      <h1>バリデーション付きフォーム</h1>    
      <div>    
        <input    
          type="text"    
          name="name"    
          value={values.name}    
          onChange={handleChange}    
          placeholder="名前を入力"    
        />    
        {errors.name && <p style={{ color: 'red' }}>{errors.name}</p>}    
        <input    
          type="email"    
          name="email"    
          value={values.email}    
          onChange={handleChange}    
          placeholder="メールアドレスを入力"    
        />    
        {errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}    
        <button onClick={handleSubmit}>送信</button>    
        <button onClick={resetForm}>リセット</button>    
      </div>    
      <p>{message}</p>    
    </div>    
  );    
}    
    
export default App;    

このコードでは、useFormにバリデーション機能を追加し、エラーメッセージを表示します。ブラウザで確認すると、名前が空またはメールアドレスが無効な場合にエラーメッセージが表示されます。自分でバリデーションルール(例:名前の長さ制限)や新しいカスタムフック(例:トグルスイッチ用)を追加し、カスタムフックの可能性を試してください。

次のステップに向けて

本記事では、カスタムフックを設計し、フォーム管理やデータ取得のロジックを再利用可能にしました。カスタムフックは、コードのモジュール性と再利用性を高める強力なツールです。第12回では、Reactアプリケーションのパフォーマンス最適化と本番環境へのデプロイ方法を学び、完成したアプリケーションを公開します。これにより、実際のプロジェクトに応用可能なスキルが完成します。

おすすめの記事