1
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?

More than 1 year has passed since last update.

(React Hooks) カウントアップ・カウントダウンを実装してみた

Last updated at Posted at 2022-02-25

はじめに

React Hooksでタイマー機能を実装しました。(useCounter.tsx ※コードはこちら)
demoのgifのようにカウントアップとカウントダウンの両パターンに対応してます。

コード全体はこちら↓(demoに示したもの)

demo

カウントアップとカウントダウンの両方に対応
movie2.gif

使い方

step must/want 説明
(★1)useCounter.tsxを読み込み must カウントを表示したいtsxファイルに読み込む
(★2)必要な変数、関数を取り込む must 必要な変数、関数を取り込む ※補足参照
(★3)カウントを表示 must カウントを表示したい箇所にcountを記載
(★4)カウント終了を検知 want カウント終了を検知してなにかする

※(★1)~(★4)はコード内の記載と対応

[補足] (★2)必要な変数、関数を取り込む

  • 読み込み例
    最低限、countとstartCountがあればOK
const {count, isTimerEnd, initCount, startCount} = useCounter();
  • 取り込める変数
名前 Type 概要
count number カウント情報
isTimerEnd boolean true: カウントが終了した
false:カウント中 or リセット状態
  • 取り込める関数
名前 引数・返り値 概要
initCount f(引数なし):返り値なし カウントをリセット(countを0にして、isTimerEndをfalseとする)
startCount f( upLimit?:number, downLimit?:number, direction?:string):返り値なし
upLimit:カウント上限
downLimit:カウント下限
direction:どちらにカウントするか("up"の場合、下限⇒上限に向かってカウント、"down"の場合、上限⇒下限に向かってカウント)
カウントを開始する

(startCountの呼び出し例)

//引数なし:10⇒0のカウントダウンになる(デフォルト設定)
//デフォルト設定は、useCounter.tsx(4,5,6行目で変更可)
startCount();

//100⇒0のカウントダウン
startCount(100, 0, "down");

//0⇒100のカウントアップ
startCount(100, 0, "up");

呼び出し元のコード例(demoで示したもの)

App.tsx
import React from 'react';
import './App.css';
import { useEffect, useState } from 'react';
import useCounter from './useCounter' //(★1)useCounter.tsxを読み込み

const EndDom:React.VFC<{isShow:boolean}>= (props) => {
  if(props.isShow) { return <p className="color-red">カウント終了!</p> }
  return <></>
}

function App() {
  const {count, isTimerEnd, initCount, startCount} = useCounter(); //(★2)必要な変数、関数を取り込む
  const [startTime, setStartTime] = useState<number>(0);
  const [endTime, setEndTime] = useState<number>(0);

  const start = (startTime:number, endTime:number) => {
    const direction = (startTime>endTime) ? "down" : "up";
    const upLimit = (direction==="down") ? startTime : endTime;
    const downLimit = (direction==="down") ? endTime : startTime;

    startCount(upLimit, downLimit, direction)
  }

  const reset = () => {
    initCount();
  }

  const handleChangeStartTime = (e:React.ChangeEvent<HTMLInputElement>) => {
    setStartTime(parseInt(e.target.value));
  }

  const handleChangeEndTime = (e:React.ChangeEvent<HTMLInputElement>) => {
    setEndTime(parseInt(e.target.value));
  }

  return (
    <div className="App">
      <main>
        <section className='counter-input mb10'>
          <input onChange={(e)=>handleChangeStartTime(e)} type="text" placeholder='開始時間(sec)'/>
          <input onChange={(e)=>handleChangeEndTime(e)} type="text" placeholder='終了時間(sec)'/>
        </section>
        <section className='counter-button'>
          <button onClick={()=>start(startTime, endTime)} className='button mb10'>START</button>
          <button onClick={()=>reset()} className='button mb10' disabled={(isTimerEnd===true) ? false : true}>RESET</button>
        </section>
        <section className={(isTimerEnd===true) ? 'counter-value color-red' : 'counter-value'}>
          {count} //(★3)カウントを表示
        </section>
        <EndDom isShow={isTimerEnd===true}/> //(★4)カウントが終了したら何かする
      </main>
    </div>
  );
}

export default App;


useCounter.tsx

useCounter.tsx
import { useEffect, useState } from "react";

//10秒⇒0秒のカウントダウンがデフォルト
//デフォルト変更可
const UPLIMIT = 10;
const DOWNLIMIT = 0;
const DIRECTION = "down";

const useCounter = () => {
    const [timeEnd, setTimeEnd] = useState(0);//終了時間
    const [direction, setDirection] = useState("down");//カウント方向
    const [count, setCount] = useState(0);//カウント情報
    const [timerFlag, setTimerFlag] = useState(false);//カウント開始フラグ
    const [isTimerEnd, setIsTimerEnd] = useState(false);//カウント終了フラグ

    useEffect(() => {
      if (timerFlag) {
        const id = setTimeout(() => {
          setCount((beforecount) => {
            console.log('timer・・・')
            if(direction==="up"){
              if(beforecount===timeEnd-1) {
                setTimerFlag(false);
                setIsTimerEnd(true);
                return (beforecount + 1);
              }
              if(beforecount<timeEnd){
                return (beforecount + 1);
              }
              return beforecount;
            }else{
              if(beforecount===timeEnd+1) {
                setTimerFlag(false);
                setIsTimerEnd(true);
                return (beforecount - 1);
              }
              if(beforecount>timeEnd){
                return (beforecount - 1);
              }
              return beforecount;
            }
          });
        }, 1000);
        return () => clearTimeout(id);
      }
      return;
    },[count, timerFlag]);

    //開始時間セット
    const setStart = (upLimit:number, downLimit:number, direction:string) => {
      const start = (direction==="up") ? downLimit : upLimit;
      setCount(start)
    }

    //終了時間セット
    const setEnd = (upLimit:number, downLimit:number, direction:string) => {
      const end = (direction==="up") ? upLimit : downLimit;
      setTimeEnd(end);
    }

    //タイマースタート
    const startCount = (upLimit:number=UPLIMIT, downLimit:number=DOWNLIMIT, direction:string=DIRECTION) => {
      console.log('start count');
      setStart(upLimit, downLimit, direction);//開始時間セット
      setEnd(upLimit, downLimit, direction);//終了時間セット
      setDirection(direction);//カウントアップ("up") or カウントダウン("down")をセット
      setIsTimerEnd(false);//カウント終了フラグをfalse
      setTimerFlag(true);//タイマー開始
    };

    const initCount = () => {
      console.log('init count!');
      setIsTimerEnd(false);
      setCount(0);
    }

    return { count, isTimerEnd , initCount, startCount };
};

export default useCounter;
1
0
1

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
1
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?