【入門編】 タイマー処理(setTimeout/setInterval)の制御 – React実践ガイド

こんにちは!Reactの学習、楽しく進めていますか?
画面がサクサク動いて、自分の思い通りに表示が変わっていくのを見ると、「おっ、なんかすごいぞ!」ってワクワクしますよね。

でも、Reactを触り始めて少し経つと、必ずと言っていいほどぶつかる「大きな壁」があるんです。そう、それが今回お話しする`useEffect`とタイマー処理(`setTimeout`や`setInterval`)の組み合わせです。

「画面を開いた後にカウントダウンを始めたいだけなのに、なぜかタイマーが何重にも動いてバグる……」
「ページを移動したのに、裏でずっとタイマーが動き続けてエラーが出る……」

そんな経験、ありませんか?
大丈夫ですよ、安心してください。最初は誰もが「なんだこれ呪文か?」って頭を抱えます。私も昔は盛大にやらかしていました。

今回は、身近な例えを交えながら、このタイマーの制御方法を一緒にスッキリと紐解いていきましょう!

—

1. タイマー処理は「キッチンタイマー」だと思っておこう

まず、`setTimeout`(指定時間後に1回だけ実行)や、`setInterval`(指定時間ごとに繰り返し実行)を扱うときの心構えから。

これらは、いわば「キッチンタイマー」です。

料理をするときに「3分後にパスタ茹で上がりのアラームを鳴らそう」とタイマーをセットしますよね。あのタイマー、もし料理が終わってキッチンを離れるとき、どうしますか?
そのまま放置して外出したりしませんよね。うるさいし、電気代(いや、電池か!)ももったいない。ちゃんと「ピピッ」と止めて、棚に片付けますよね。

Reactのコンポーネント(画面の部品)もこれとまったく同じなんです。
画面に出てきたときはタイマーを「カチッとセット」する。でも、その画面から別のページに移動したり、部品が消えたりするときは、必ずタイマーを「オフにする(クリアする)」というお片付けが必要なんです。

このお片付けをサボると、Reactの裏側で「もう誰もいない部屋に向かって、ずっとアラームを鳴らし続ける幽霊タイマー」が暴れ回ることになります。これが、バグの正体です。

—

2. `useEffect`ってなぁに?(おまじないの正体)

Reactで「画面が表示されたタイミングで何かしたい!」というときに使うのが、`useEffect`というフック(機能)です。

書き方の基本はこんな形をしています。

useEffect(() => {
// ここに「やりたいこと(タイマーのセットなど)」を書く

return () => {
// ここに「お片付け(クリーンアップ関数)」を書く
};
}, [/ 依存配列 /]);

この「returnの中身」がめちゃくちゃ重要です。こここそが、先ほどお話しした「キッチンタイマーを止めるボタン」なんですよ。

  • コンポーネントが出てきたとき:`useEffect`の中のメイン処理が動く(タイマーON!)
  • コンポーネントが消えるとき(または次に切り替わる前):`return`の中身が動く(タイマーOFF!)

この「セットと片付けのワンセット」を覚えるだけで、Reactの副作用(画面描画以外の処理)の怖さはグッと減ります。

—

3. 【実践】1秒ごとに数字が増えるカウンターを作ってみよう

百聞は一見に如かず。実際に、1秒ごとにカウントが増えていく時計のようなコンポーネントを作ってみましょう。
お使いのエディタにそのまま貼り付けて動かせるコードを用意しました。コメントを読んで、流れを感じてみてくださいね。

import React, { useState, useEffect } from ‘react’;

export default function TimerComponent() {
// 秒数を管理する「count」という状態(State)を作ります。初期値は0です。
const [count, setCount] = useState(0);

useEffect(() => {
// 【1. タイマーのセット(お仕事開始)】
// 1000ミリ秒(=1秒)ごとに、countの数字を「今より1増やす」処理を予約します。
const timerId = setInterval(() => {
// 古い状態を安全に更新するために、コールバック関数形式(prev => …)を使っています
setCount((prevCount) => prevCount + 1);
}, 1000);

// デバッグ用にコンソールを出してみます
console.log(‘タイマーをセットしました! ID:’, timerId);

// 【2. お片付け(クリーンアップ関数)】
// このコンポーネントが画面から消えるとき、または再レンダリングされる直前に実行されます。
return () => {
clearInterval(timerId); // 動かしていたタイマーをしっかり止めます!
console.log(‘タイマーをキレイに片付けました!’);
};
}, []); // 依存配列が空([])なので、「画面に最初に現れた時」にだけタイマーをセットします。

return (

);
}

このコードの優しいポイント

1. `setInterval`で返ってくる「タイマーのID(管理番号のようなもの)」を、`timerId`という変数にしっかり捕まえています。
2. `return () => { clearInterval(timerId); }` の部分で、そのIDを指定して「このタイマーを止めて!」と指示を出しています。これがタイマー制御のゴールデンルールです。

—

4. よくあるつまずきポイント:「あれ?タイマーが2倍速で動く!?」

初心者の頃、私もやりましたし、質問サイトでも本当によく見かける現象があります。
それは、依存配列(`useEffect`の末尾にある `[]` の部分)の書き方を間違えて、タイマーが何個も同時に動き出してしまう現象です。

例えば、もし依存配列に `[count]` なんて書いちゃったらどうなるでしょう?

// ⚠️ 悪い例(やってしまいがちなミス)
useEffect(() => {
const timerId = setInterval(() => {
setCount(count + 1);
}, 1000);

return () => clearInterval(timerId);
}, [count]); // ← ここに count を入れちゃった!

これだと、「`count`が変わるたびに、`useEffect`が一度お片付けをして、新しいタイマーをセットし直す」という動きになります。
結果として、1秒ごとにタイマーが生まれ変わり、処理が重なったり、予期せぬ挙動を引き起こしたりします。「なんか数字の進み方がおかしいぞ……?」となったら、大体この依存配列の指定ミスが原因です。

基本のルール:
タイマーの中で現在の状態(State)を安全に更新したいときは、さきほどのサンプルコードのように 関数型アップデート(`setCount(prev => prev + 1)`) を使って、依存配列をスッキリ空(`[]`)にしてあげるのが、最も安全でトラブルの少ない王道テクニックです。

—

5. まとめ

いかがでしたでしょうか?
`useEffect`でのタイマー制御、難しく感じられたかもしれませんが、要点を整理するととてもシンプルです。

  • タイマーを使ったら、必ずセット(`setInterval` / `setTimeout`)と片付け(`clearInterval` / `clearTimeout`)をセットで書く。
  • お片付けは、`useEffect`の中の `return () => { … }` に書く。
  • コンポーネントが消えるときや切り替わるときに、裏で暴れないようにするためのお作法。

最初はコードのお作法が多くて戸惑うことも多いですが、一つひとつ意味を理解していくと、Reactは本当に素直で頼もしい相棒になってくれます。

あなたのフロントエンド開発の旅が、少しでも楽しく、スムーズなものになりますように。
つまずいたときは、いつでもまたこの記事に帰ってきてくださいね。応援しています!

コメント

タイトルとURLをコピーしました