🐁

【React】ストップウォッチを作ってみる

2023/04/28に公開

プロジェクト作成

npx create-react-app timer-app

完成コード

import React, { useState, useRef } from 'react';

function Stopwatch() {
    const [time, setTime] = useState(0);
    const [isRunning, setIsRunning] = useState(false);
    const intervalRef = useRef(null);

    function handleStart() {
        setIsRunning(true);
        intervalRef.current = setInterval(() => {
            setTime(prevTime => prevTime + 10);
        }, 10);
    }

    function handlePause() {
        clearInterval(intervalRef.current);
        setIsRunning(false);
    }

    function handleReset() {
        clearInterval(intervalRef.current);
        setIsRunning(false);
        setTime(0);
    }

    const milliseconds = `0${(time % 1000) / 10}`.slice(-2);
    const seconds = `0${Math.floor(time / 1000) % 60}`.slice(-2);
    const minutes = `0${Math.floor(time / 60000) % 60}`.slice(-2);
    const hours = `0${Math.floor(time / 3600000)}`.slice(-2);

    return (
        <div>
            <h1>Stopwatch</h1>
            <p>{hours}:{minutes}:{seconds}:{milliseconds}</p>
            {isRunning ? (
                <button onClick={handlePause}>Pause</button>
            ) : (
                <button onClick={handleStart}>Start</button>
            )}
            <button onClick={handleReset}>Reset</button>
        </div>
    );
}

export default Stopwatch;

解説

import React, { useState, useRef } from 'react';

このコードは、Reactでコンポーネントを作成する際によく使用されるパターンです。
import文を使用して、ReactとuseStateとuseRefという2つのフックをインポートしています。

ReactはReactライブラリを使用するために必要なものであり、
useStateフックはコンポーネント内で状態を管理するために使用されます。
useRefフックは、コンポーネントのレンダリング中に値を保存するために使用されます。
この例では、ストップウォッチのinterval IDを保存するために使用されています。


function Stopwatch() {
    const [time, setTime] = useState(0);
    const [isRunning, setIsRunning] = useState(false);
    const intervalRef = useRef(null);

このコードは、Reactの関数コンポーネントであるStopwatchを定義しています。
useStateフックを使用して、timeとisRunningという2つの状態を管理しています。
timeはストップウォッチの経過時間を表し、isRunningはストップウォッチが実行中かどうかを表します。useStateフックは、現在の状態とその状態を更新するための関数を返します。

useRefフックを使用して、intervalRefという新しい変数を定義しています。
intervalRefは、setInterval関数で使用されるinterval IDを保存するために使用されます。useRefフックは、レンダリングの間に値を保存するために使用されます。ここでは、intervalRefをnullで初期化しています。


    function handleStart() {
        setIsRunning(true);
        intervalRef.current = setInterval(() => {
            setTime(prevTime => prevTime + 10);
        }, 10);
    }

このコードは、handleStartという関数を定義しています。
この関数は、ストップウォッチを開始するために使用されます。

まず、setIsRunningを使用してisRunning状態をtrueに設定します。
次に、setIntervalを使用して、ストップウォッチを更新するためのインターバルを設定します。setIntervalは、指定された間隔で関数を繰り返し呼び出します。ここでは、10ミリ秒ごとに、setTimeを使用してtime状態を更新します。
setTimeには、現在のtime値に10を加算した値を渡しています。
setTimeには、前のtime値が引数として渡されます。
これは、Reactが状態を更新するときに前の値を保持するために使用されるパターンです。


    function handlePause() {
        clearInterval(intervalRef.current);
        setIsRunning(false);
    }

このコードは、handlePauseという関数を定義しています。
この関数は、ストップウォッチを一時停止するために使用されます。

まず、clearIntervalを使用して、setIntervalで設定されたインターバルをクリアします。intervalRef.currentには、現在のinterval IDが保存されています。
次に、setIsRunningを使用して、isRunning状態をfalseに設定します。
これにより、ストップウォッチが実行中でないことが示されます。


    function handleReset() {
        clearInterval(intervalRef.current);
        setIsRunning(false);
        setTime(0);
    }

このコードは、handleResetという関数を定義しています。
この関数は、ストップウォッチをリセットするために使用されます。

まず、clearIntervalを使用して、setIntervalで設定されたインターバルをクリアします。
次に、setIsRunningを使用して、isRunning状態をfalseに設定します。
これにより、ストップウォッチが実行中でないことが示されます。
最後に、setTimeを使用して、time状態を0に設定します。
これにより、ストップウォッチの経過時間がリセットされます。


    const milliseconds = `0${(time % 1000) / 10}`.slice(-2);
    const seconds = `0${Math.floor(time / 1000) % 60}`.slice(-2);
    const minutes = `0${Math.floor(time / 60000) % 60}`.slice(-2);
    const hours = `0${Math.floor(time / 3600000)}`.slice(-2);

このコードは、現在のストップウォッチの時間(time状態)を、ミリ秒、秒、分、時間の単位に分割します。

まず、time状態を1000で割った余りを計算して、ミリ秒を取得します。その後、0${}の形式で文字列を作成し、slice(-2)で2桁に整形します。

次に、time状態を1000で割り、floor関数を使用して、秒数を取得します。60で割った余りを計算して、秒数を60未満の数値に整形します。

同様に、time状態を60000で割り、floor関数を使用して、分数を取得します。60で割った余りを計算して、分数を60未満の数値に整形します。

最後に、time状態を3600000で割り、floor関数を使用して、時間を取得します。0${}の形式で文字列を作成し、slice(-2)で2桁に整形します。



    return (
        <div>
            <h1>Stopwatch</h1>
            <p>{hours}:{minutes}:{seconds}:{milliseconds}</p>
            {isRunning ? (
                <button onClick={handlePause}>Pause</button>
            ) : (
                <button onClick={handleStart}>Start</button>
            )}
            <button onClick={handleReset}>Reset</button>
        </div>
    );
}

このコードは、ストップウォッチのUIをレンダリングするためのreturn文を含んでいます。

最初の行では、<h1>タグを使用して、「Stopwatch」という見出しを表示しています。

次の行では、計測中のストップウォッチの時間を表示しています。
これは、先に定義した時間単位の文字列を含んだ文字列を使用して、<p>タグで
レンダリングされます。

その後、isRunning状態に応じて、開始/一時停止ボタンが表示されます。
isRunningがtrueの場合、handlePause関数を呼び出す「Pause」ボタンが表示されます。
それ以外の場合、handleStart関数を呼び出す「Start」ボタンが表示されます。

最後に、「Reset」ボタンが表示されます。このボタンがクリックされた場合、handleReset関数が呼び出され、ストップウォッチがリセットされます。


export default Stopwatch;

このコードは、Stopwatchコンポーネントを他のファイルからインポートできるようにするためのエクスポート文です。

export default Stopwatchという文言は、Stopwatchコンポーネントをデフォルトでエクスポートすることを示しています。これにより、他のファイルでStopwatchコンポーネントをインポートする際に、import Stopwatch from './Stopwatch'のように簡単にインポートすることができます。

Discussion