【ITニュース解説】Building a Smarter Custom Hook in React: Auto-Refreshing Data with Cleanup

2025年09月04日に「Dev.to」が公開したITニュース「Building a Smarter Custom Hook in React: Auto-Refreshing Data with Cleanup」について初心者にもわかりやすいように丁寧に解説しています。

作成日: 更新日:

ITニュース概要

Reactのカスタムフックを使えば、データ取得と指定間隔での自動更新ロジックをまとめ、再利用性を高められる。useEffectの依存配列を適切に設定することで、変更時に自動で更新・クリーンアップが行われ、コードをきれいに保てる。

ITニュース解説

Reactアプリケーションを開発する上で、共通の処理をまとめ、コードをきれいに保つことは非常に重要になる。ここで「カスタムフック」という強力な機能が登場する。カスタムフックは、Reactのコンポーネント間で再利用したいロジック、つまり特定の処理の流れやデータの管理方法を一つにまとめるための仕組みである。この記事では、Web APIからデータを取得し、一定時間ごとに自動でそのデータを更新し、さらに不要になった処理を適切に停止する「賢い」カスタムフックの作り方と、その背後にある重要な考え方を解説する。

なぜ、データ取得のロジックを直接アプリケーションのメインコンポーネント(例えばAppコンポーネント)に書かずに、わざわざカスタムフックとして切り出す必要があるのだろうか。もし、Appコンポーネント内にデータ取得の処理を直接記述した場合、UI(ユーザーインターフェース)を表示するためのロジックと、Webからデータを取得するロジックが混ざり合ってしまう。これにより、コードのどこで何が行われているのかが分かりにくくなり、後から修正したり、他の開発者が理解したりする際の負担が大きくなる。また、もし別のコンポーネントでも同じ種類のデータを取得する必要が生じた場合、同じデータ取得のコードを何度もコピー&ペーストすることになる。これは保守性を著しく低下させ、バグの原因にもなりやすい。さらに、定期的にデータを更新する処理(インターバル)や、コンポーネントが画面から消えたときにそのインターバルを停止する処理(クリーンアップ)を直接Appコンポーネントで管理すると、コードがさらに複雑になり、本来のUI表示ロジックが見えにくくなってしまう。

カスタムフックとしてデータ取得と自動更新のロジックを切り出すことで、これらの問題を解決できる。具体的には、このロジックを「useTodos」という名前のカスタムフックとして作成する。このフックは、Web APIからTodoリストのデータを取得するだけでなく、指定された時間間隔で自動的にデータを再取得して最新の状態に保つ機能を持つ。そして、コンポーネントが画面から消えるなどの場合に、不要になった自動更新処理を自動的に停止する「クリーンアップ」の機能も備える。これにより、このデータ取得と更新のロジックはどのコンポーネントからでも簡単に再利用できるようになり、コード全体が読みやすく、管理しやすくなる。つまり、コードの再利用性、清潔さ、自動的なクリーンアップ、そしてUIとデータの関心事の明確な分離といったメリットが得られるのである。

それでは、具体的なuseTodosカスタムフックのコードを見ていこう。このフックはuseEffectuseStateというReactの二つの基本的なフック、そしてWeb APIにリクエストを送るためのaxiosというライブラリを使用する。まず、useTodosフックの内部では、useStateを使って二つの状態変数を定義する。一つは取得したTodoリストを保持するtodos、もう一つはデータ取得中かどうかを示すloadingである。これらはそれぞれ、Todoリストのデータと、データ取得中のUI表示(例えば「Loading...」といったメッセージ)を制御するために使われる。

次に、useEffectフックが核心的な役割を果たす。useEffectは、コンポーネントのレンダリング後に特定の副作用(データ取得、イベントリスナーの設定、タイマーなど)を実行するために使用される。useEffectの内部では、まずfetchTodosという関数を定義する。この関数はaxios.getを使って指定されたURL(https://dummyjson.com/todos)からTodoリストのデータを非同期に取得し、取得が完了したらsetTodostodosの状態を更新し、setLoading(false)でデータ取得が完了したことを示す。

useEffectの本体が実行されると、まずfetchTodos()が一度呼び出され、コンポーネントが画面に表示された直後に一度だけデータが取得される。その後、setIntervalというJavaScriptの関数を使って、fetchTodos関数を一定の間隔で繰り返し実行するように設定する。この間隔は、useTodosフックの引数として渡されるtimeout(秒単位)に基づいて設定される。例えば、timeoutが6であれば、6秒ごとにfetchTodosが実行され、Todoリストが自動で更新されることになる。

useEffectには、コンポーネントが画面から削除される際や、特定の依存関係が変更された際に実行される「クリーンアップ関数」を返す機能がある。このクリーンアップ関数は、return () => clearInterval(reRunning);として定義されている。ここでreRunningsetIntervalが返したタイマーIDである。このクリーンアップ関数が返すclearInterval(reRunning)は、設定したインターバルタイマーを停止する役割を持つ。これにより、コンポーネントが画面から消えたり、timeoutの値が変更されたりした場合に、不要になった古いインターバル処理が停止され、メモリリークなどの問題を防ぐことができる。これは、リソースを適切に管理するための非常に重要な仕組みだ。

useEffectの二つ目の引数として渡される「依存配列(dependencies array)」は、このフックの挙動を理解する上で最も重要なポイントの一つである。useTodosフックでは、この依存配列に[timeout]が指定されている。この[timeout]という指定は、Reactに対して「もしtimeout変数の値が前回と変わったら、useEffectの中の処理を最初からやり直してほしい」という指示を出しているのと同じだ。

もしこの依存配列が空の配列[]であった場合、useEffectはコンポーネントが画面に表示された時に一度だけ実行され、それ以降はtimeoutの値が変わっても再実行されない。つまり、一度設定されたインターバルタイマーは、timeoutが更新されても古い値のまま動作し続けてしまう。これは、例えばユーザーが「更新間隔」を変更したとしても、アプリがその変更を反映しないという不具合につながる。しかし、依存配列に[timeout]を含めることで、timeoutの値が変更されるたびに、古いインターバルタイマーがクリーンアップ関数によって停止され、新しいtimeoutの値に基づいて新しいインターバルタイマーが設定される。これにより、自動更新のロジックは常に最新のtimeout設定に従って動作するようになるのだ。この依存配列の適切な管理こそが、柔軟でバグの少ないReactアプリケーションを作る鍵となる。

このuseTodosカスタムフックを実際のアプリケーションで使う方法は非常にシンプルである。メインのAppコンポーネントでは、単にconst { todos, loading } = useTodos(6);のように呼び出すだけでよい。これでAppコンポーネントは、6秒ごとに自動更新されるTodoリストのデータと、そのデータが現在取得中かどうかを示す情報を受け取ることができる。もしloadingtrueであれば「Loading...」と表示し、データが取得できたらtodosリストを画面に表示する。Todoリストの各項目はTrackという別のコンポーネントに渡され、その中でtodo.idtodo.todoの内容が表示される。

このようにカスタムフックを利用することで、Appコンポーネントのコードは非常にすっきりする。データ取得のためのaxiosの呼び出しも、インターバル設定のためのsetIntervalの複雑な管理も一切記述する必要がなくなる。Appコンポーネントは、純粋に受け取ったデータをどのように表示するかというUIのロジックだけに集中できるのだ。これは「関心事の分離」というソフトウェア開発の重要な原則を体現しており、コードの可読性、保守性、拡張性を大きく向上させる。

結局のところ、useEffectの依存配列は、Reactがいつエフェクトを再実行するかを監視するための「監視リスト」のようなものである。このリストに含まれる値が変更されるたびに、Reactはまず以前のエフェクトによって設定されたクリーンアップ関数(ここではclearInterval)を実行し、古い処理を停止する。その後、新しい値を使ってuseEffectの本体を再度実行し、新しい処理(新しいインターバルタイマーの設定)を開始する。この仕組みを理解し、適切に利用することで、リソースの無駄遣いを防ぎ、アプリケーションを常に最新の状態で、かつバグなく動作させることができる。

今回のカスタムフックは、データ取得、インターバルによる自動更新、適切なクリーンアップ、そしてuseEffectの依存配列による動的な振る舞いの制御という、React開発における重要な要素を組み合わせた好例と言える。重要な要点は二つある。一つは、useEffectの依存配列が空の[]である場合、エフェクトはコンポーネントが画面に表示された時に一度だけ実行されるという点。もう一つは、[timeout]のように動的な値を依存配列に含めることで、その値が変更されるたびにエフェクトが再実行され、アプリケーションが柔軟にその変更に適応できるという点である。これらの知識は、Reactで堅牢なアプリケーションを構築していく上で不可欠な基礎となるだろう。

関連コンテンツ

【ITニュース解説】Building a Smarter Custom Hook in React: Auto-Refreshing Data with Cleanup | いっしー@Webエンジニア