0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【学習記録】useEffectで作るタイマー処理

0
Posted at

Reactでタイマーを実装する際、useEffectsetIntervalを組み合わせるパターンはよく使われます。
しかし、クリーンアップ処理を怠るとメモリリークが発生したり、意図しない動作になることがあります。

本記事では、以下のコードをもとにuseEffectを使ったタイマー実装の仕組みをまとめました。

useEffect(() => {
  if (isRunning) {
    const intervalId = setInterval(() => {
      setTime((time) => time + 1)
    }, 1000)
    return () => {
      clearInterval(intervalId)
    }
  }
}, [isRunning])

このコードの目的は、isRunningtrueの間、1秒ごとにtimeを+1し続けるタイマーを実装することです。

💡各部分の詳細解説

1. useEffectの依存配列 [isRunning]

useEffect(() => {
  // ...
}, [isRunning]) // ← 依存配列

依存配列にisRunningを指定することで、isRunningの値が変化するたびにエフェクトが再実行されます。

isRunningの変化 エフェクトの動作
falsetrue エフェクトが再実行される(タイマー開始)
truefalse クリーンアップ後、エフェクトが再実行される(タイマー停止)

2. setIntervalでタイマーを開始

if (isRunning) {
  const intervalId = setInterval(() => {
    setTime((time) => time + 1)
  }, 1000)
}
  • isRunningtrueのときだけsetIntervalを実行します
  • setIntervalは第2引数のミリ秒(ここでは1000ms = 1秒)ごとに第1引数の関数を繰り返し呼び出します
  • 戻り値のintervalIdは、後でタイマーを止めるために使います

3. 関数型更新 setTime((time) => time + 1)

setTimeの書き方には2通りあります。

// ❌ 直接値を渡す(バグが起きやすい)
setTime(time + 1)

// ✅ 関数型更新(推奨)
setTime((time) => time + 1)

なぜ関数型更新を使うのか?

setIntervalのコールバック内では、クロージャによって最初にキャプチャされたtimeの値(古い値) を参照し続けてしまいます。

関数型更新を使うと、Reactが常に最新の状態を引数として渡してくれるため、古い値を参照するバグを防げます。

4. クリーンアップ関数 return () => { clearInterval(intervalId) }

return () => {
  clearInterval(intervalId)
}

useEffectから関数をreturnすることで、クリーンアップ関数を登録できます。

クリーンアップ関数は以下のタイミングで自動的に呼ばれます。

タイミング 説明
isRunningが変化したとき 古いエフェクトをクリーンアップしてから新しいエフェクトを実行
コンポーネントがアンマウントされたとき タイマーを確実に停止してメモリリークを防ぐ

クリーンアップ関数がないと?

isRunningfalseになってもインターバルが止まらず、バックグラウンドでsetTimeが呼ばれ続けます。これがメモリリーク予期しない再レンダリングの原因になります。


動作フロー

【タイマー開始】
isRunning: false → true
  └─ useEffect 実行
       └─ setInterval 開始(毎秒 time++)

【タイマー停止】
isRunning: true → false
  └─ クリーンアップ関数が呼ばれる
       └─ clearInterval でインターバル停止
  └─ useEffect 再実行(if(isRunning)がfalseなので何もしない)

【コンポーネント消滅】
アンマウント時
  └─ クリーンアップ関数が呼ばれる
       └─ clearInterval でインターバル停止

✅️ポイントまとめ

項目 ポイント
依存配列 [isRunning] isRunningが変わるたびにエフェクトが再実行される
setInterval 一定間隔で繰り返し処理を実行する
関数型更新 (time) => time + 1 クロージャによる古い値参照バグを防ぐ
クリーンアップ関数 メモリリーク・多重起動を防ぐ。必ず書く!

📖おわりに

useEffect + setInterval のタイマー実装は、一見シンプルですが、クリーンアップ処理関数型更新を正しく使わないと微妙なバグの原因になることがわかりました。

この記事が参考になれば幸いです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?