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?

【React】1秒ずつ進むはずのタイマーが2秒ずつ進む?

Posted at

発生した問題

Reactに触り始めのころ、実際にしたことです。

画面を開いたときに動く、1秒ずつ進むタイマーを作ろうとして、以下のようなコードを書いたのですが、実際にはタイマーが2秒ずつに進んでしまう、ということがありました。

App.tsx
import { useState, useEffect } from 'react'

function App() {
  const [time, setTime] = useState(0);

  /**
   * 最初の処理
   */
  useEffect(() => {
    // 1秒ごとに1ずつ足していくインターバル
    setInterval(() => {
      setTime((prevTime) => prevTime + 1);
    }, 1000);
  }, []);

  return (
    <>
      <div>{time}</div>
    </>
  )
}

export default App

解決法1

この原因としては、React の StrictMode により、マウント時にuseEffectの処理が2回行われ、タイマーが2回起動していたからです。
StrictModeについての参考:https://qiita.com/asahina820/items/665c55594cfd55e6f14a

この問題は useEffect の return にタイマーの終了処理を書くことで解決します。(クリーンアップ関数)

App.tsx
import { useState, useEffect } from 'react'

function App() {
  const [time, setTime] = useState(0);

  /**
   * 最初の処理
   */
  useEffect(() => {
    // 1秒ごとに1ずつ足していくインターバル
    const timerId =setInterval(() => {
      setTime((prevTime) => prevTime + 1);
    }, 1000);

    // タイマーのクリーン
    return () => {
      clearInterval(timerId);
    };
  }, []);

  return (
    <>
      <div>{time}</div>
    </>
  )
}

export default App

こうすることで、StrictMode による2回目のマウント時には、前のタイマーはクリーンされ停止し、1秒ずつの動作になります。

解決法2

またはタイマー用のstateを追加し、それを依存配列に持つuseEffect内でタイマー処理を行うことでも、正常に動作します。

App.tsx
import { useState, useEffect } from 'react'

function App() {
  const [time, setTime] = useState(0);
  // タイマー用state
  const [isTimerStop, setIsTimerStop] = useState(true);

  /**
   * 最初の処理
   */
  useEffect(() => {
    // タイマー用stateの値を変更
    setIsTimerStop(false);
  }, []);

  /**
   * タイマー処理
   */
    useEffect(() => {
      if (isTimerStop) {
        // タイマーが止まっている場合
        return;
      }

      // 1秒ごとに1ずつ足していくインターバル
      const timerId = setInterval(() => {
        setTime((prevTime) => prevTime + 1);
      }, 1000);

      // タイマーのクリーン
      //(なくても1秒ずつの動作にはなるが、タイマーを一時停止させる必要がある場合は必須)
      return () => {
        clearInterval(timerId);
      };
    }, [isTimerStop]);// 依存配列にタイマー用stateを持たせる

  return (
    <>
      <div>{time}</div>
    </>
  )
}

export default App

このように書いた場合は、StrictModeにより setIsTimerStop() は2回動作しますが、useEffectでは依存配列の値が同じ場合は処理が動かないので、タイマーが2回起動することはなくなります。

以上です。

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?