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

useEffectフックで副作用とデータ取得を管理(第7回)

By Author
image
Reactアプリケーションでは、状態やプロパティの変更に応じてUIを更新するだけでなく、外部リソースとのやり取りや副作用の管理が必要です。`useEffect`フックは、これらの副作用を処理するための強力なツールです。本連載の第7回では、`useEffect`フックの基本を学び、コンポーネントのライフサイクルを管理し、APIからデータを取得する方法をハンズオン形式で習得します。第1回で構築したReact開発環境、第2回のJSX、第3回のProps、第4回の`useState`、第5回のイベントハンドリング、第6回のリストレンダリングの知識を基に、macOS環境で開発を進めます。本記事は、プログラミングの基礎知識を持つ読者を対象としています。

useEffectフックの基本概念

useEffectは、コンポーネントのレンダリング後に副作用(例:データ取得、タイマー設定、DOM操作)を処理するためのフックです。副作用とは、コンポーネントの状態やプロパティに依存しつつ、純粋なレンダリング以外の動作を指します。useEffectの基本構文は以下の通りです。

import React, { useEffect } from 'react';    
    
useEffect(() => {    
  // 副作用の処理    
  return () => {    
    // クリーンアップ処理(オプション)    
  };    
}, [依存配列]);    

useEffectは、コールバック関数(副作用の処理)と依存配列を受け取ります。依存配列に指定した値が変更されたとき、または初回レンダリング時にコールバックが実行されます。依存配列が空([])の場合、初回レンダリング時のみ実行されます。クリーンアップ関数を返すことで、コンポーネントのアンマウント時や再実行前にリソースを解放できます。

ハンズオン:タイマーアプリケーションの作成

useEffectの基本を理解するため、経過時間を表示するタイマーアプリケーションを作成します。第1回で作成したプロジェクト(my-react-app)のsrc/App.jsを以下のように編集します。

import React, { useState, useEffect } from 'react';    
import './App.css';    
    
function App() {    
  const [seconds, setSeconds] = useState(0);    
    
  useEffect(() => {    
    const interval = setInterval(() => {    
      setSeconds((prev) => prev + 1);    
    }, 1000);    
    
    return () => clearInterval(interval);    
  }, []);    
    
  return (    
    <div className="app-container">    
      <h1>タイマーアプリケーション</h1>    
      <p>経過時間: {seconds}秒</p>    
    </div>    
  );    
}    
    
export default App;    

このコードでは、useStateseconds状態を管理し、useEffect内でsetIntervalを使用して1秒ごとにsecondsを更新します。依存配列が空([])なので、タイマーはコンポーネントのマウント時(初回レンダリング)に開始されます。クリーンアップ関数でclearIntervalを呼び出し、コンポーネントのアンマウント時にタイマーを停止します。src/App.cssは第6回までのスタイルをそのまま使用します。

.app-container {    
  text-align: center;    
  padding: 20px;    
  font-family: Arial, sans-serif;    
}    

開発サーバー(npm start)が起動している状態で保存すると、ブラウザに経過時間が秒単位で表示されます。ページをリロードしてもタイマーが継続的に動作し、コンポーネントを別の画面に切り替える(または開発サーバーを停止する)とクリーンアップが実行されることを確認できます。

APIからデータを取得する

useEffectの一般的な用途は、外部APIからデータを取得することです。ここでは、JSONPlaceholderという公開APIを使用して、ユーザーデータを取得する例を試します。App.jsを以下のように変更します。

import React, { useState, useEffect } from 'react';    
import './App.css';    
    
function App() {    
  const [users, setUsers] = useState([]);    
  const [loading, setLoading] = useState(true);    
    
  useEffect(() => {    
    fetch('https://jsonplaceholder.typicode.com/users')    
      .then((response) => response.json())    
      .then((data) => {    
        setUsers(data);    
        setLoading(false);    
      })    
      .catch((error) => {    
        console.error('データ取得エラー:', error);    
        setLoading(false);    
      });    
  }, []);    
    
  return (    
    <div className="app-container">    
      <h1>ユーザーリスト</h1>    
      {loading ? (    
        <p>読み込み中...</p>    
      ) : (    
        <ul>    
          {users.map((user) => (    
            <li key={user.id}>{user.name}</li>    
          ))}    
        </ul>    
      )}    
    </div>    
  );    
}    
    
export default App;    

このコードでは、fetchを使用してJSONPlaceholderからユーザーデータを取得し、users状態に保存します。loading状態でデータ取得中のUIを管理し、取得完了後にユーザーの名前をリスト表示します。key属性をuser.idで指定し、リストレンダリングの効率を確保しています。App.cssにリストのスタイルを追加します。

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;    
}    

ブラウザで確認すると、最初に「読み込み中...」が表示され、データ取得後にユーザーの名前リストが表示されます。エラーハンドリングも含まれており、ネットワークエラーが発生した場合にコンソールにエラーが記録されます。

ハンズオン:副作用のカスタマイズ

useEffectの柔軟性を体感するため、読者自身で機能を追加します。以下の課題を試してください。App.jsを編集し、ユーザーデータから特定のユーザー(例:IDが1のユーザー)のみを表示するボタンを追加します。以下は一例です。

import React, { useState, useEffect } from 'react';    
import './App.css';    
    
function App() {    
  const [users, setUsers] = useState([]);    
  const [selectedUser, setSelectedUser] = useState(null);    
  const [loading, setLoading] = useState(true);    
    
  useEffect(() => {    
    fetch('https://jsonplaceholder.typicode.com/users')    
      .then((response) => response.json())    
      .then((data) => {    
        setUsers(data);    
        setLoading(false);    
      })    
      .catch((error) => {    
        console.error('データ取得エラー:', error);    
        setLoading(false);    
      });    
  }, []);    
    
  const showFirstUser = () => {    
    const firstUser = users.find((user) => user.id === 1);    
    setSelectedUser(firstUser);    
  };    
    
  return (    
    <div className="app-container">    
      <h1>ユーザーデータ</h1>    
      {loading ? (    
        <p>読み込み中...</p>    
      ) : (    
        <>    
          <button onClick={showFirstUser}>ID:1のユーザーを表示</button>    
          {selectedUser ? (    
            <p>選択されたユーザー: {selectedUser.name}</p>    
          ) : (    
            <ul>    
              {users.map((user) => (    
                <li key={user.id}>{user.name}</li>    
              ))}    
            </ul>    
          )}    
        </>    
      )}    
    </div>    
  );    
}    
    
export default App;    

このコードでは、ボタンクリックでIDが1のユーザーを表示し、選択されたユーザーがいればその情報だけを表示します。App.cssにボタンのスタイルを追加します。

button {    
  margin: 10px;    
  padding: 10px 20px;    
  font-size: 16px;    
  cursor: pointer;    
}    

ブラウザで確認すると、ボタンをクリックすると特定のユーザーの名前が表示されます。自分で異なる条件(例:特定のユーザー名の検索)や表示項目(例:メールアドレス)を追加し、useEffectの活用方法を試してください。

次のステップに向けて

本記事では、useEffectフックを使用して副作用を管理し、タイマーやAPIデータ取得を実装しました。useEffectは、外部リソースとの連携やライフサイクル管理に不可欠なツールです。第8回では、フォームの構築に焦点を当て、制御されたコンポーネントを使用してユーザー入力を効率的に処理する方法を学びます。これにより、ユーザーとの対話性をさらに強化できます。

おすすめの記事