【React×useRef】ストップウォッチを自作!useEffectで簡単タイマー実装ガイド

プログラミング

Reactでストップウォッチを作る理由

時間を扱うアプリの重要性とは?

結論:ストップウォッチはReactの基本スキルを学ぶ絶好の題材です。

以下のようなシーンで時間の計測が使われています。

  • 運動記録(ランニングタイマーなど)
  • 勉強時間の管理(ポモドーロ式など)
  • 工場や業務現場での作業時間測定
  • 子どものゲームや遊び時間の制限

こうした「時間を測る機能」は、Webアプリだけでなく実社会でも広く使われています。

Reactでストップウォッチを作ることで、画面の状態管理・時間制御・副作用の扱いなど、多くのスキルが身につきます。


useRefとuseEffectの使い分け

useRefはなぜ必要なのか?

結論:画面の再描画を避けつつ、値を保持するために使います。

通常の状態管理(useState)は値が変わるたびに画面が更新されます。

しかし、ストップウォッチでは、1秒ごとに状態を更新すると負荷が高くなります。

そのため、useRefを使って「再描画しないが保持される値」として時間やタイマーIDを記録します。

useEffectはどこで使う?

結論:初期化やクリーンアップなど、タイミングを調整するために使います。

useEffectは以下のような処理に適しています。

  • アプリ起動時に処理を1度だけ実行
  • コンポーネントが破棄されるときにタイマーを止める
  • ボタン押下に応じてイベントを切り替える

ストップウォッチのように時間と密接に関わる機能では、useEffectの使い方が非常に重要です。


Reactでストップウォッチの表示を作る

画面とボタンの構成を考える

結論:シンプルに「時間」と「3つのボタン」を用意します。

以下が基本の構成です:

  • 経過時間の表示(00:00:00)
  • スタートボタン
  • ストップボタン
  • リセットボタン
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Stopwatch() {
  const [time, setTime] = useState(0); // 経過時間(秒)
  const timerRef = useRef(null);
  return (
    <div>
      <h2>ストップウォッチ</h2>
      <h1>{formatTime(time)}</h1>
      <button>スタート</button>
      <button>ストップ</button>
      <button>リセット</button>
    </div>
  );
}
function formatTime(seconds) {
  const h = String(Math.floor(seconds / 3600)).padStart(2, '0');
  const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
  const s = String(seconds % 60).padStart(2, '0');
  return `${h}:${m}:${s}`;
}

スタート・ストップ・リセット機能の実装

実際の動きをReactで表現する

結論:setIntervalで時間を進め、useRefで管理すれば簡単に実現できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Stopwatch() {
  const [time, setTime] = useState(0);
  const timerRef = useRef(null);
  const start = () => {
    if (timerRef.current !== null) return;
    timerRef.current = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
  };
  const stop = () => {
    clearInterval(timerRef.current);
    timerRef.current = null;
  };
  const reset = () => {
    stop();
    setTime(0);
  };
  useEffect(() => {
    return () => clearInterval(timerRef.current); // アンマウント時に停止
  }, []);
  return (
    <div>
      <h2>ストップウォッチ</h2>
      <h1>{formatTime(time)}</h1>
      <button onClick={start}>スタート</button>
      <button onClick={stop}>ストップ</button>
      <button onClick={reset}>リセット</button>
    </div>
  );
}

応用機能と改良ポイント

さらに良いストップウォッチにするには?

結論:以下のような追加機能でより実用的になります。

  • 一時停止と再開機能
  • ラップタイム(区切り時間)の記録
  • CSSで見た目を改善
  • 時間の最大値やアラームの設定

時間や副作用の処理を学ぶのにちょうど良い題材として、ストップウォッチは非常におすすめです。


完成形コード一覧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, { useState, useRef, useEffect } from 'react';
function Stopwatch() {
  const [time, setTime] = useState(0);
  const timerRef = useRef(null);
  const start = () => {
    if (timerRef.current !== null) return;
    timerRef.current = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);
  };
  const stop = () => {
    clearInterval(timerRef.current);
    timerRef.current = null;
  };
  const reset = () => {
    stop();
    setTime(0);
  };
  useEffect(() => {
    return () => clearInterval(timerRef.current);
  }, []);
  return (
    <div>
      <h2>ストップウォッチ</h2>
      <h1>{formatTime(time)}</h1>
      <button onClick={start}>スタート</button>
      <button onClick={stop}>ストップ</button>
      <button onClick={reset}>リセット</button>
    </div>
  );
}
function formatTime(seconds) {
  const h = String(Math.floor(seconds / 3600)).padStart(2, '0');
  const m = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
  const s = String(seconds % 60).padStart(2, '0');
  return `${h}:${m}:${s}`;
}
export default Stopwatch;

まとめ

ReactのuseRefuseEffectを使って、ストップウォッチアプリをゼロから作る方法を学んできました。

おさらいとして:

  • useRefは再描画せずに値を保持する道具
  • useEffectは処理の開始・終了タイミングを管理する手段
  • Reactを使えば、誰でも本格的なストップウォッチが作れる

ぜひ実際にコードを試しながら、Reactの「時間」と「状態」の扱い方に慣れてみてください。


関連リンク:


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