はじめに
スクロールイベントを用いる実装では、イベントによる負荷が発生しますよね。
スクロールの高さを取得して、その高さによってなんらかのアクションをしたい時。
スクロールの高さはかなり細かく取ってくれるので、負荷が高いです。
1mmスクロール位置を動かしただけでスクロールイベントが発火するとか(言い過ぎかな)
パフォーマンス低下やバグの恐れがあるので、少しでも負荷を抑える実装が必要になります。
今回は、reacthooksの中でthrottleを使うやり方についてまとめます。
throttle(lodashライブラリ)
lodashライブラリは、便利な関数がたっぷり収納されている有名な巨大ライブラリです。
JSのディープコピー問題を解決してくれる関数があったり。
その中に、throttleというのがあります。
今回はそちらを使用するのですが、throttleとはイベント負荷を抑えることができる関数です。
具体的には、指定した時間が過ぎるまでは次のイベントを発生しないようにしてくれます。
簡単に処理を間引いてくれるのです。便利!
間引くというとtimeoutを使って自力で書くこともできますが、面倒なのでライブラリを活用します。
throtttle以外に、debounceという同じくイベント負荷を抑える関数が存在します。
よく比較されます。
こちらは、「大量の処理をグループ化して処理の最後に一回だけ実行する」というもの。
使ったことないのですが、比較については色々な記事で紹介されています。
一部抜粋
Debouncing and Throttling Explained Through Examples
Imagine you are in an elevator. The doors begin to close, and suddenly another person tries to get on. The elevator doesn’t begin its function to change floors, the doors open again. Now it happens again with another person. The elevator is delaying its function (moving floors), but optimizing its resources.
エレベーター無理矢理入ってくる人たまにいますね。
throttleとdebounceの違い。覚え方も考えてみた
インストールする
lodash丸ごとインストールすると容量が大きいので、個別でインストールします。
yarn add lodash.throttle
TS用のモジュールもインストール
yarn add @types/lodash.throttle
イベントを間引く
実際の処理です。
export const useSomething = () => {
// 省略
const someFunc = () => {
const onScroll = () => {
const currentPosition = Math.max(
window.pageYOffset,
document.documentElement.scrollTop,
document.body.scrollTop
)
console.log('位置取得', currentPosition)
if (currentPosition >= somePosition) {
// この位置に来たら何かしたい
}
}
// スクロールイベントを0.4秒毎に間引く
const throttledCount = throttle(onScroll, 400)
document.addEventListener('scroll', throttledCount)
return () => document.removeEventListener('scroll', throttledCount)
}
}
useEffect(() => {
someFunc()
}, [])
// 省略
}
あとは上記のカスタムhookをページやコンポーネントで呼び出します。
イベントを間引いてる様子をcodesandboxで作ったので、触ってみてください。
- console.logに位置取得の様子が表示されています。
- 比較の際は、use-scroll.tsで31行目の
normalScrollEvent()
のコメントアウトを外して、代わりに30行目のthrottledScrollEvent()
をコメントアウトしてください
まとめ
パフォーマンスのためにもイベント負荷を意識するのは大事ですね。
以外に調べてみると、lodash×Reactの記事がなかったのですが、普段どのようなイベント負荷をしているのか是非コメント頂けると嬉しいです!
また、画面の遅延に影響するReactのstate/propsによる再レンダリングの負荷なども意識していきたいです。
なのでこの辺も知見が溜まったらまとめようかなと思います。
参考
Using Throttling and Debouncing with React hooks
カッコいいけど遅すぎる! JSのスクロールイベントを改善するベストな方法
Lodashのファイルサイズを削減する方法