【React】スクロールダウン・アップでボタンの非表示・表示を切り替える
概要
スクロールアップ時に表示され、スクロールダウン時に非表示になるButtonコンポーネントをつくりたい
要件・流れ
- スクロールアップ ---ボタン表示
スクロールダウン ---ボタン非表示
以前のスクロール位置を
useState
で保持する- ボタンの非表示・表示は
display: block
display: none
を切り替えて管理する - 前回のスクロール位置が現在のスクロール位置よりも大きい場合、ボタンを表示する。これは、ユーザーがページを上にスクロールしていることと同意
export const ScrollButton = () => { // ボタン表示・非表示の状態 const [isVisible, setIsVisible] = useState(true); // 前回のスクロール位置 const [prevScrollPosition, setPrevScrollPosition] = useState(0); const handleScroll = () => { // 現在のスクロール位置を取得 const currentScrollPos = window.scrollY; // 前回のスクロール位置が現在のスクロール位置よりも大きい場合、ボタンを表示する const isVisible = prevScrollPosition > currentScrollPos; setIsVisible(isVisible); setPrevScrollPosition(currentScrollPos); }; useEffect(() => { window.addEventListener("scroll", handleScroll); // クリーンアップ return () => { window.removeEventListener("scroll", handleScroll); }; }, [handleScroll]); return ( <button style={{ display: isVisible ? "block" : "none" }}> Float Button </button> ); };
Refactor
現在だとスクロールイベントが高頻度で発生してしまうため、パフォーマンスに影響を与えてしまう可能性がある。
そのため、debounce
関数を使って、イベントを間引きして実行するように修正していく。
import React, { useState, useEffect } from "react"; import { debounce } from "lodash"; export const ScrollButton = () => { const [isVisible, setIsVisible] = useState(true); const [prevScrollPosition, setPrevScrollPosition] = useState(0); const handleScroll = () => { const currentScrollPos = window.scrollY; const isVisible = prevScrollPosition > currentScrollPos; setIsVisible(isVisible); setPrevScrollPosition(currentScrollPos); console.log("scroll"); }; const debounceHandleScroll = debounce(handleScroll, 100); useEffect(() => { window.addEventListener("scroll", debounceHandleScroll); return () => { window.removeEventListener("scroll", debounceHandleScroll); }; }, [debounceHandleScroll]); return ( <button style={{ display: isVisible ? "block" : "none" }}> Float Button </button> ); };
bounce関数使用前
bounce関数使用後