はじめに
React Hooksでタイマー機能を実装しました。(useCounter.tsx ※コードはこちら)
demoのgifのようにカウントアップとカウントダウンの両パターンに対応してます。
コード全体はこちら↓(demoに示したもの)
demo
使い方
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;