📚

React Hooks入門: 関数コンポーネントの力を最大限に活用しよう

2023/10/02に公開

はじめに

React は、コンポーネントベースのライブラリとして非常に人気があります。React 16.8 以降、関数コンポーネント内で状態を管理するための新しいツール、React Hooks が導入されました。この記事では、React Hooks の基本から応用まで、関数コンポーネントを効果的に活用する方法を詳しく解説します。

useState

useState は、React コンポーネント内で状態を追跡・更新するためのフックです。useState は、2 つの要素を返します。

  1. 状態変数(State Variable): これは現在の状態の値を保持します。例えば、カウンターアプリケーションではカウンターの値が状態変数になります。

  2. 状態更新関数(State Updater Function): これは状態を更新するための関数です。この関数を呼び出すことで、新しい状態を設定できます。

これにより、関数コンポーネント内で状態を管理し、その変更がレンダリングにどのように影響を与えるかを制御できます。

useState の基本的な使い方

useState を使った基本的なパターンは次の通りです:

import { useState } from "react";

export const Example = () => {
  //   1. 状態変数countを宣言し、初期値0を設定
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      {/* 2. ボタンクリック時に状態を更新 */}
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

上記のコードでは、useState を使用して状態変数 count とその更新関数 setCount を宣言しています。初期値として 0 を設定し、ボタンをクリックすると setCount を呼び出して状態を更新します。この変更はコンポーネントの再レンダリングをトリガーし、新しい状態を画面に表示します。

useEffect

React アプリケーションは、ユーザーインターフェースの構築や更新に関する主要なタスクを処理するために、コンポーネントを使用します。しかし、アプリケーションにはコンポーネントの描画だけでなく、データの読み込みや外部イベントの処理など、副作用の処理も含まれます。このような副作用を管理するために、React は useEffect を提供しています。

useEffect の基本的な使い方

import { useEffect } from 'react';

export const Example = () => {
  useEffect(() => {
    // 副作用の処理
    // ...

    // クリーンアップ関数(オプション)
    return () => {
      // クリーンアップ処理
      // ...
    };
  }, [dependencies]);

  return (
    // JSXコンポーネントのレンダリング
    // ...
  );
}

上記のコードでは、以下の要素が重要です:

  1. useEffect 関数内で副作用の処理を定義します。これは API 呼び出し、イベントリスナーの設定などの処理を含むことができます。

  2. オプションとして、return ステートメントを使用してクリーンアップ関数を提供できます。このクリーンアップ関数は、コンポーネントがアンマウントされる前や再レンダリング時に実行されます。クリーンアップ関数は、リソースの解放やイベントリスナーの削除など、副作用をクリーンアップするために使用されます。

  3. フックの第二引数として、dependencies(依存関係)の配列を指定できます。この配列に含まれる値が変更された場合、副作用の処理が再実行されます。依存関係が空の場合、副作用は初回のレンダリング時にのみ実行されます。

クリーンアップ関数の役割

クリーンアップ関数は、副作用の後始末をするための重要な仕組みです。例えば、イベントリスナーの削除やリソースの解放など、副作用がコンポーネントのライフサイクル内で発生する際に、これらのリソースが適切に管理されることを保証します。クリーンアップ関数を使用することで、メモリーリークや不必要なリソースの占有を防ぎ、アプリケーションのパフォーマンスと安定性を向上させることができます。

依存リストの重要性

useEffect では、依存リスト(dependencies)を指定することができます。この依存リストに含まれる値が変更された場合にのみ、副作用の処理が再実行されます。依存リストを正確に指定することは、不必要な再レンダリングや副作用の無駄な実行を防ぐ上で重要です。

例えば、ある状況でのみ特定のデータをフェッチしたい場合、そのデータを依存リストに指定することで、そのデータが変更されたときだけ副作用を実行できます。これにより、効率的なコードの実行とリソースの節約が可能になります。

依存リストの管理は、React アプリケーションのパフォーマンスを最適化する上で欠かせないスキルの一つです。

useEffect の実例

API 呼び出しの例

import { useState, useEffect } from "react";

export const DataFetching = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("https://api.example.com/data")
      .then((response) => response.json())
      .then((data) => setData(data));
  }, []); // 依存関係が空なので初回のレンダリング時のみ実行

  return (
    <div>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

この例では、useEffect を使用して API からデータをフェッチしています。依存関係が空のため、副作用は初回のレンダリング時にのみ実行されます。

イベントリスナーの設定とクリーンアップ

import { useState, useEffect } from "react";

export const MousePosition = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const handleMouseMove = (e) => {
      setPosition({ x: e.clientX, y: e.clientY });
    };

    // マウス移動イベントのリスナーを設定
    window.addEventListener("mousemove", handleMouseMove);

    // クリーンアップ関数
    return () => {
      // コンポーネントがアンマウントされたときにイベントリスナーを削除
      window.removeEventListener("mousemove", handleMouseMove);
    };
  }, []); // 依存関係が空なので初回のレンダリング時のみ実行

  return (
    <div>
      <p>Mouse Position:</p>
      <p>
        X: {position.x}, Y: {position.y}
      </p>
    </div>
  );
};

この例では、マウスの位置をトラッキングしています。useEffect を使用してマウス移動イベントのリスナーを設定し、クリーンアップ関数でイベントリスナーを削除しています。

useContext

React アプリケーション内でデータをコンポーネント間で共有する必要がある場合、useContext は便利なツールです。
useContext を使用してコンテキストを設定し、コンポーネントツリー内でデータを効率的に共有する方法について解説します。

useContext の基本的な使い方

import { useContext } from "react";

// コンテキストを作成
const MyContext = React.createContext();

export const ParentComponent = () => {
  // コンテキストを使用してデータを提供
  const data = { message: "Hello from Context!" };

  return (
    <MyContext.Provider value={data}>
      <ChildComponent />
    </MyContext.Provider>
  );
};

export const ChildComponent = () => {
  // コンテキストを使用してデータを受け取る
  const contextData = useContext(MyContext);

  return <p>{contextData.message}</p>;
};

上記のコードでは、以下のポイントに注目してください:

  1. MyContext というコンテキストを作成します。これは、データを提供し、受け取るための仕組みです。

  2. ParentComponent 内で MyContext.Provider を使用してコンテキストを提供します。提供するデータは value プロパティに設定されます。

  3. ChildComponent 内で useContext を使用してコンテキストからデータを受け取ります。これにより、ParentComponent からデータを受け取ることができます。

useContext の実例

ユーザーのテーマ設定をアプリケーション全体で共有する場合

import { useContext, useState } from "react";

const ThemeContext = React.createContext();

export const App = () => {
  const [theme, setTheme] = useState("light");

  const toggleTheme = () => {
    setTheme(theme === "light" ? "dark" : "light");
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <Header />
      <Content />
    </ThemeContext.Provider>
  );
};

const Header = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <header>
      <h1>My App</h1>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
  );
};

const Content = () => {
  const { theme } = useContext(ThemeContext);

  return (
    <div className={`content ${theme}`}>
      <p>This is the content area.</p>
    </div>
  );
};

この例では、テーマ情報をコンテキストを介して Header と Content コンポーネントで共有しています。toggleTheme 関数を呼び出すことで、テーマを切り替えることができます。

カスタムフック

カスタムフックは、React コンポーネントで使用するロジックを分離し、再利用可能な関数として抽象化する手法です。これにより、同じロジックを複数のコンポーネントで使用できるようになり、コードのメンテナンスが容易になります。カスタムフックは、"use"で始まる関数名で定義され、React のフックと同じルールに従います。

カスタムフックの作成方法

カスタムフックを作成するためには、以下のステップを実行します:

  1. 新しい TypeScript ファイルを作成し、カスタムフックの関数を定義します。例:useCustomHook.ts

  2. カスタムフック内で必要なロジックを実装します。これには状態管理、副作用処理、API 呼び出し、イベント処理などが含まれます。

  3. 必要に応じて、カスタムフックの返り値としてデータや関数を公開します。これにより、コンポーネント内で使用できる API を定義します。

カスタムフックの再利用例

以下は、カウンターのロジックをカスタムフックとして抽象化し、それを複数のコンポーネントで再利用する例です。

カスタムフックを書く
まず、TypeScript を使用してカスタムフックを書きます。例として、カウンターのカスタムフックを作成します。

useCounter.ts
import { useState } from 'react';

type CounterHookReturnType = {
  count: number;
  increment: () => void;
  decrement: () => void;
};

export const useCounter = (initialValue: number = 0): CounterHookReturnType => {
  const [count, setCount] = useState<number>(initialValue);

  const increment = (): void => {
    setCount(count + 1);
  };

  const decrement = (): void => {
    setCount(count - 1);
  };

  return { count, increment, decrement };
};

カスタムフックを使用するコンポーネント
次に、カスタムフックを使用する React コンポーネントを作成します

CounterComponent.tsx
import useCounter from './useCounter';

export const CounterComponent = () => {
  const { count, increment, decrement } = useCounter(0);

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

ベストプラクティスと注意事項

React Hooks を使用する際には、以下のベストプラクティスに注意してください。

  1. フックはトップレベルで呼び出す: フックはループ、条件分岐、ネストされた関数内で呼び出してはいけません。常にトップレベルで呼び出して下さい。

  2. 依存リストを正確に指定する: useEffect などのフックを使用する際には、依存リストを正確に指定しましょう。不要な再レンダリングを避けるために依存関係を明示的に設定します。

  3. カスタムフックを活用する: カスタムフックを作成してロジックを再利用しましょう。これによりコードがシンプルになり、保守性が向上します。

  4. パフォーマンスを意識する: レンダリングの最適化に注意しましょう。不要な再レンダリングを避け、コンポーネントのパフォーマンスを向上させるために React の最適化技術を活用しましょう。

  5. React.memo の活用: React.memo を使用して、コンポーネントの不要な再レンダリングを防ぎましょう。特に、コンポーネントの props が変更されない場合に、再レンダリングを回避できます。

  6. useCallback の使用: useCallback を使用して、コールバック関数をメモ化しましょう。これにより、親コンポーネントから子コンポーネントに渡されるコールバック関数が不要に再生成されるのを防ぎ、パフォーマンスを向上させます。

  7. エラーハンドリング: アプリケーションで発生する可能性のあるエラーに対処する方法について考えましょう。エラーハンドリングの適切な方法は、アプリケーションのタイプや要件によって異なります。

  8. テスト: ユニットテストや統合テストを行い、アプリケーションの品質を確保しましょう。React Hooks のコンポーネントロジックが正しく機能することを確認するためにテストを活用しましょう。

まとめ

この記事では、React Hooks を使用した関数コンポーネント内での状態管理や副作用処理、コンテキストの設定、カスタムフックの作成について解説しました。

useState

  • useState を使用すると、関数コンポーネント内で状態変数とその更新関数を宣言できます。
  • 状態変数は現在の状態の値を保持し、更新関数を呼び出すことで状態を更新できます。
  • このフックを使用することで、コンポーネントの状態を管理し、それがレンダリングにどのように影響を与えるかを制御できます。

useEffect

  • useEffect は副作用の処理を行うために使用されます。副作用の例として API 呼び出しやイベントリスナーの設定が挙げられます。
  • useEffect はコンポーネントのレンダリング後に副作用の処理を実行し、必要に応じてクリーンアップ処理を提供できます。
  • 依存関係の配列を指定することで、特定の値が変更された場合にのみ副作用を再実行できます。

useContext

  • useContext を使用すると、コンポーネント間でデータを効率的に共有できます。
  • コンテキストを作成し、Provider を使用してデータを提供し、Consumer を使用してデータを受け取ります。
  • これにより、テーマ設定などのデータをアプリケーション全体で共有することができます。

カスタムフック

  • カスタムフックは、コンポーネント間でロジックを再利用するための強力な方法です。ロジックを分離し、再利用可能な関数として抽象化します。
  • カスタムフックは、useXXX という命名規則に従って命名され、ロジックをカプセル化します。これによりコードが整理され、再利用性が向上します。

最後に、React Hooks を使用する際に注意すべきベストプラクティスとパフォーマンスの最適化についても触れました。

この記事では、React の関数コンポーネントでの開発における基本的な要素から、高度なテクニックまで幅広くカバーしました。React Hooks は、コンポーネントベースのアプリケーションの開発を効率的に行うための強力なツールであり、それを正しく活用することで、アプリケーションの品質やメンテナンス性を向上させることができます。

次のステップとして、実際のプロジェクトで React Hooks を使用してアプリケーションを開発してみて下さい。また、React の公式ドキュメントやコミュニティのリソースを活用していきましょう。

この記事を参考にして、React アプリケーションの開発をよりスムーズに進めていただければ幸いです。

参考文献

最新の情報や詳細なドキュメントにアクセスするために、以下の参考文献を参照してください

https://react.dev/reference/react

Arsaga Developers Blog

Discussion