はじめに
1 秒ごとに処理に応じてちょっとずつ変化していくコンポーネントが作りたかった です
プログレスバーの拡張版みたいな...
setInterval を使って実装して見たけど、setInterval の引数に渡した関数内では State が参照できないようで、以下のように setState 内で無理矢理条件分岐を作らないといけないです
const intervalValue = setInterval(() => {
setCount((count) => {
if (count === 5) {
clearInterval(intervalValue)
return count
}
return count + 1
})
}, 1000)
もっといい方法はないか考えていたらふと Generator の事を思い出したので実装してみることにしました
実装
お試しなのでシンプルにカウントアップするものを実装
作るもの
- ボタンを押すと 1 秒おきにカウントアップされる
- 比較のため Genetretor+Iteretor を使ったものと Interval を使ったものどっちも実装してみる
コード
See the Pen poeVOGB by あずは (@azuha) on CodePen.
解説
非同期ジェネレータ関数を利用して、count が 5 になるまで 1 秒毎に yield でカウントアップしていきます
ジェネレータ関数を利用するとその戻り値は Generetor 型となり、yield で指定した値を持つ、いわゆるイテラブルなオブジェクトとして返却されます
wait 関数を自作し、await で待機処理を作るために async 関数にしています
const countUp = async function* (first) {
let count = first;
while(count < 5) {
await wait(1000)
count++
yield count
}
}
非同期ジェネレータ関数の戻り値は AsyncGeneretor 型となり、受け取ったイテラブルなオブジェクトは for await...of 文で取り出すことができます。
for await (const value of countUp(0)) {
setCount(value)
}
ES2018 まで for 文の中で非同期処理は行えなかったみたいですが、for await...of 文が追加されてからは上記のような書き方ができるようになりました
Interval を使用する場合 setInterval の戻り値を保持する State を用意しないといけなかった反面、ジェネレータ関数を利用すると State は必要なく簡潔に書けることがわかると思います
後書き
今まであまり触れてこなかったジェネレータ関数を利用することで欲しかった「ちょっとずつ変化していくコンポーネント」を実装することができました
割と最近出た構文(だと思っている)ですが、こういう新しい技術は覚えておくと今まで難しかったことがあっさり解決することがあるんだな〜と勉強になりました