第2回
useEffectの基本:外部との同期・依存配列・クリーンアップ
ページタイトルを同期する完成サンプルでuseEffectの役割を学びます。イベント処理との違い、依存配列、クリーンアップ、StrictModeでの確認方法を解説します。
制作・運営:React Guide 運営者
Effectが必要になる境界
useEffect は、Reactの表示を外部の仕組みと同期させるHookです。タイマー、購読、ブラウザAPIなど、コンポーネントを表示している間に接続しておきたい相手があるときに使います。
計算できる値をstateへコピーする用途や、クリック時の送信処理には、まずEffectを使わない実装を考えます。
| やりたいこと | 置く場所 |
|---|---|
| 商品数×単価を表示する | レンダー中の計算 |
| 送信ボタンを押したら保存する | イベントハンドラー |
| 表示中のページ名をdocument.titleに反映する | Effect |
| 表示中だけタイマーを動かす | Effectとcleanup |
ページ名をブラウザのタブに反映する
以下は src/App.tsx を置き換えて試せる例です。React 19系のVite React+TypeScriptプロジェクトを使います。
import { useEffect, useState } from 'react'
function PageTitle({ title }: { title: string }) {
useEffect(() => {
const previousTitle = document.title
document.title = title
return () => { document.title = previousTitle }
}, [title])
return <h1>{title}</h1>
}
export default function App() {
const [title, setTitle] = useState('学習メモ')
const [visible, setVisible] = useState(true)
return (
<main>
<label>ページ名<input value={title} onChange={e => setTitle(e.target.value)} /></label>
<button onClick={() => setVisible(v => !v)}>表示切り替え</button>
{visible && <PageTitle title={title} />}
</main>
)
}title を読んでいるので依存配列は [title] です。ここを [] にすると、入力欄を変更してもタブの文字は最初のままになります。元のタイトルへ戻すcleanupは、この例のように1つのコンポーネントがタイトルを管理するときのものです。複数の部品で同じdocument.titleを同時に管理しないでください。
いつ実行されるか
| 指定 | 実行される条件 |
|---|---|
| 依存配列なし | コンポーネントの各コミット後 |
[] |
マウント時。依存値の変更による再実行はない |
[title] |
マウント時と、titleがObject.isで異なるコミット後 |
依存値が変わると、古い値でcleanup、新しい値でsetupの順に実行します。アンマウント時にもcleanupします。開発時のStrictModeでは初期のsetup→cleanup→setupを追加で試す場合があります。「一生に1回だけ実行する」保証ではありません。
Effectはサーバーレンダリング中には動きません。また「コミット後」と「必ずブラウザが描画し終えた後」は同じではありません。操作に起因するEffectはpaintより前に動く場合もあります。
データ取得も外部との同期
Effect内でfetchする場合、初回のデータがない表示、HTTPエラー、取得中のID切り替えを扱う必要があります。コードを短くするためにこれらを外したものを、完成版として使わないでください。
- Promiseを直接Effectから返せない理由と書き方は async/awaitの解説へ。
- 古い取得結果を使わない実装は cleanupの回へ。
- 共通化した取得Hookは カスタムHookの回へ。
フレームワークのloaderなどで取得できる場合は、その仕組みも検討します。Effectでの取得は、すべてのReactアプリに必須の方法ではありません。
自分で確認する
- 入力欄を「買い物メモ」に変更し、見出しとブラウザのタブが両方変わるか確認します。
- 表示切り替えでPageTitleを外し、タブが元の文字に戻るか確認します。
- ローカルでだけ依存配列を
[]に変え、入力後にどちらが更新されなくなるか予測して試します。確認後は[title]に戻します。
同じテーマの教材
【解決】useEffectでasync/awaitを使う正しい方法と注意点
useEffectに直接asyncを付けてはいけない理由と、正しいasync/awaitの書き方を解説。内部関数として定義する方法・クリーンアップとの組み合わせ・競合状態(race condition)の防ぎ方まで実例付きで説明します。
useEffectのクリーンアップ:メモリリークを防ぐ正しい実装
useEffectのクリーンアップ関数を解説します。タイマー・イベントリスナー・非同期処理のキャンセルなど、アンマウント時に必要なクリーンアップの実装方法をメモリリーク防止の観点から学べます。
useEffectの依存配列:正しい指定方法とよくある落とし穴
useEffectの依存配列を徹底解説。空配列・値の指定・オブジェクトや関数を依存配列に含めたときの問題と解決策、exhaustive-depsルールの使い方を実例付きで説明します。