はじめに
よくあるカウントダウンタイマーをReact Hook Timerを使って
作ってみた時に、onExpireでハマりました
問題
ドキュメントによるとonExpireはコールバック関数
つまり、カウントダウンが終わった後に呼び出される関数ということらしい
ポモドーロタイマーなので、Time
が終わった後、breakTime
が自動で再生されるように、
下記のように書いたところ、
const { seconds, minutes, hours, isRunning, start, pause, resume, restart } = useTimer({
expiryTimestamp: getExpiryTimestamp(getDuration()),
autoStart: false,
onExpire: () => {
if (mode === "work") {
setMode("break");
restart(getExpiryTimestamp(breakTime), true);
} else {
setMode("work");
restart(getExpiryTimestamp(time), true);
}
},
});
死ぬほどバグる、、、
何でやねん、、、
バグらなかった状況としてはユーザー側が、休憩のスタートボタンを押下するなどのアクションを追加した時だけでした。
ただ、これだと要件が合わないので、一旦無視してuseEffectでやることにしました
解決方法
実際のコードはこちら
const { seconds, minutes, hours, isRunning, start, pause, resume, restart } = useTimer({
expiryTimestamp: getExpiryTimestamp(getDuration()),
autoStart: false,
onExpire: () => {
if (mode === "work") {
setMode("break");
} else {
setMode("work");
}
},
});
useEffect(() => {
if (firstRender) {
setFirstRender(false);
return;
}
if (mode === "work") {
restart(getExpiryTimestamp(time), true);
console.log(mode);
} else {
restart(getExpiryTimestamp(breakTime), true);
setStop(true);
console.log(mode);
}
}, [mode]);
ただ、useEffect
を使用するとonExpire
が原因で、mode
が切り替わりまくって、
レンダリングが繰り返されてしまうので、firstRender
をつけます
最初のレンダリング時には何もしないでただreturn
するっていう条件をつけてあげるだけのフラグです
おわりに
作るものが特殊なせいなのかreact-hook-timerはめちゃめちゃ使いづらかった
多分、ポモドーロタイマーには合わないんだろうなと思ったので、
どこかで自作のタイマーを作った方が早そうな気がしました。
現状は困っていないのでリファクタリングしながらやってみます