Reactのカスタムフックでスクロール位置を管理する方法|初心者向けガイド
生徒
「ページのスクロール位置を取得して処理したいです。どのように実装すれば良いですか?」
先生
「カスタムフックを作ると便利です。スクロール位置の取得や監視を共通化できます。」
生徒
「共通化って具体的にはどんなメリットがありますか?」
先生
「コードの重複を減らし、テストや保守を楽にできます。順番に見ていきましょう。」
1. スクロール位置とイベントの基礎知識
スクロール位置とは、ユーザーがページのどこまで画面を動かして閲覧しているかを示す情報です。 長いページを下に読み進めると、このスクロール位置の数値は少しずつ大きくなっていきます。 ReactやJavaScriptでは、この位置を使って表示内容を切り替えたり、動きを付けたりできます。
ブラウザでは window.scrollY や window.pageYOffset を使うことで、
現在の縦方向のスクロール位置を数値として取得できます。
数値はピクセル単位で返されるため、「どれくらい下にスクロールしたか」を正確に判断できます。
また、スクロールイベントは、ユーザーがマウスホイールを動かしたり、 スマートフォンで画面を指でなぞったりするたびに何度も発生します。 そのため、スクロールに反応する処理を書くときは、 必要以上に重い処理を行わないように意識することが大切です。
window.addEventListener("scroll", () => {
console.log(window.scrollY);
});
上の例は、スクロールするたびに現在のスクロール位置を表示する、とてもシンプルな仕組みです。 プログラミング未経験の方でも、「スクロールすると数字が変わる」という感覚をつかむのに役立ちます。
2. カスタムフックで管理するメリット
スクロール処理を各コンポーネントに直接書いていくと、
似たような addEventListener や状態管理のコードが何度も登場しがちです。
最初は問題なくても、ページ数や機能が増えるにつれて、
「どこに同じ処理が書かれているのか分からない」状態になりやすくなります。
そこでカスタムフックとしてスクロール処理をまとめておくと、 どの画面でも同じ書き方でスクロール位置を扱えるようになります。 修正が必要になった場合も、フックを直すだけで全体に反映されるため、 保守や調整がとても楽になります。
import React from "react";
import useScrollPosition from "./useScrollPosition";
function ScrollInfo() {
const scrollY = useScrollPosition();
return (
<p>現在のスクロール位置:{scrollY}px</p>
);
}
export default ScrollInfo;
この例では、スクロール位置を表示するだけのシンプルなコンポーネントですが、 スクロール処理そのものは一切書いていません。 「スクロール位置が欲しい」ときにフックを呼び出すだけで使えるのが、 カスタムフック最大のメリットです。
3. 基本的なカスタムフック(useScrollPosition)
ここでは、縦方向のスクロール位置を取得する、いちばん基本的なカスタムフックを作成します。 「今どれくらい下にスクロールしているか」を数値で知ることができるようになると、 表示の切り替えやボタン制御など、さまざまな場面で応用できるようになります。
カスタムフックの役割はとてもシンプルで、 「スクロールイベントを監視して、現在の位置を状態として保存する」だけです。 難しく考えず、「スクロールの数値をいつでも取り出せる箱を作る」 というイメージで理解すると分かりやすくなります。
import { useState, useEffect } from "react";
function useScrollPosition() {
const [scrollY, setScrollY] = useState(window.scrollY || 0);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY || window.pageYOffset);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return scrollY;
}
export default useScrollPosition;
このフックでは、useState を使ってスクロール位置を保存し、
useEffect の中でスクロールイベントを登録しています。
ページを動かすたびに数値が更新され、その最新の値が常に返されます。
ポイントは、スクロール処理そのものをコンポーネントに書かないことです。 これにより、どの画面でも同じフックを呼び出すだけで、 スクロール位置を簡単に使えるようになります。
import React from "react";
import useScrollPosition from "./useScrollPosition";
function ScrollValueSample() {
const scrollY = useScrollPosition();
return <p>スクロール位置は {scrollY}px です</p>;
}
export default ScrollValueSample;
このように、カスタムフックを使えば、 スクロール位置を表示する処理もとても直感的に書けます。 まずは「数値が取れるようになる」ことを目標にして、 仕組みをしっかり理解しておきましょう。
4. 実例:トップへ戻るボタンの表示制御
スクロール位置を使った代表的な例として、「トップへ戻る」ボタンがあります。 一定位置までスクロールしたときだけ表示することで、操作性が向上します。
import React from "react";
import useScrollPosition from "./useScrollPosition";
function ScrollTopButton() {
const scrollY = useScrollPosition();
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: "smooth" });
};
return (
<div>
{scrollY > 300 && (
<button onClick={scrollToTop}>トップへ戻る</button>
)}
</div>
);
}
export default ScrollTopButton;
5. パフォーマンス対策:デバウンスとスロットル
スクロールイベントは非常に頻繁に発生します。 そのまま状態更新を行うと、レンダリングが多発してしまいます。
デバウンスは「動きが止まってから処理する」方法、 スロットルは「一定間隔で処理する」方法です。 用途に応じて使い分けることが重要です。
6. スクロール方向の検出と応用
スクロールが上方向か下方向かを判定できると、 ヘッダーの表示切り替えや無限スクロールの制御に活用できます。
7. SSR・モバイル環境での注意点
サーバーサイドレンダリング環境では window が存在しません。
初期値の設定や実行環境の判定を行うことで、エラーを防げます。
モバイル端末ではスクロール挙動が異なるため、実機での確認も重要です。
8. テストとデバッグの考え方
スクロール処理はテストが難しく感じられますが、 イベントをモックすることで挙動を確認できます。
イベントリスナーが正しく解除されているかも忘れずにチェックしましょう。
9. 実務での活用シーン
スクロール位置を管理できるようになると、実際のWebサービスや業務アプリで使える場面が一気に広がります。 見た目の動きだけでなく、ユーザーの操作に合わせた自然な画面制御ができるようになるため、 使いやすさや分かりやすさの向上につながります。
特に初心者の方が最初に取り入れやすいのは、「スクロール量に応じて表示を変える」パターンです。 難しい処理をしなくても、スクロール位置の数値を条件に使うだけで、実用的な機能を実装できます。
- ページ下部まで読んだ人だけに表示する「トップへ戻る」ボタン
- スクロール方向に応じて自動で隠れるヘッダーやナビゲーション
- 画面の一番下に近づいたら追加データを読み込む無限スクロール
- どこまで読まれたかを記録するための閲覧状況やパフォーマンス分析
import React from "react";
import useScrollPosition from "./useScrollPosition";
function ScrollMessage() {
const scrollY = useScrollPosition();
return (
<div>
{scrollY > 500 ? (
<p>ここまで読んでくれてありがとうございます</p>
) : (
<p>下にスクロールして続きを読んでください</p>
)}
</div>
);
}
export default ScrollMessage;
このサンプルでは、スクロール位置が五百ピクセルを超えたかどうかで表示内容を切り替えています。 特別な処理は書いておらず、「数値を条件に使う」だけで動きのある画面を作れるのがポイントです。
このように、カスタムフックで取得したスクロール位置は、 ボタン表示、メッセージ切り替え、データ取得のきっかけなど、 さまざまな実務シーンでそのまま活用できます。 まずはシンプルな使い方から試してみるのがおすすめです。
10. フォルダ構成と命名ルール
カスタムフックは数が増えてくると管理が難しくなりやすいため、
あらかじめ置き場所と名前の付け方を決めておくことが大切です。
一般的には src/hooks フォルダを用意し、
スクロールや入力監視などの共通処理をまとめて配置します。
名前は必ず use から始めるのが基本ルールです。
これにより「これはカスタムフックだ」と一目で分かり、
Reactの仕組みとしても正しく認識されます。
役割が分かる名前を付けることで、後からコードを見返したときも理解しやすくなります。
src/
├─ components/
│ └─ ScrollTopButton.jsx
└─ hooks/
├─ useScrollPosition.js
└─ useScrollDirection.js
例えばこのようにフォルダを分けておくと、 「画面を作る部品」と「動きを管理する処理」が自然に整理されます。 初心者の方でも、どこに何を書けばよいか迷いにくくなるのがメリットです。
function useScrollPosition() {
// スクロール位置を返すフック
}
ファイル名と関数名を揃えておくことで、 読む人にとっても使う人にとっても分かりやすい構成になります。 小さなルールですが、実務ではこの積み重ねがコードの読みやすさに大きく影響します。
11. スクロール管理を理解するとできること
スクロール位置をカスタムフックで管理できるようになると、 画面の動きに合わせて自然に反応するインターフェースを作れるようになります。 単に数値を取得するだけでなく、「今ユーザーがどの位置を見ているか」を アプリ側で把握できる点が大きなポイントです。
例えば、ページをある程度読み進めた人にだけ案内メッセージを表示したり、 注意書きや補足情報をタイミングよく出したりすることも可能になります。 こうした工夫は、ユーザー体験の向上や離脱防止にもつながります。
import React from "react";
import useScrollPosition from "./useScrollPosition";
function ReadingNotice() {
const scrollY = useScrollPosition();
return (
<div>
{scrollY > 800 && <p>このページの後半まで読んでいます</p>}
</div>
);
}
export default ReadingNotice;
このサンプルでは、スクロール位置が一定以上になったときだけ メッセージを表示しています。 条件分岐にスクロール値を使うだけなので、 プログラミング未経験の方でも仕組みを理解しやすい構成です。
このように、スクロール管理を理解すると 「今どんな状態のユーザーに、何を見せるか」を考えた実装ができるようになります。 小さな工夫の積み重ねが、使いやすいReactアプリを作る近道になります。
まとめ
スクロール位置をカスタムフックで管理する重要性
本記事では、Reactにおけるスクロール位置の管理方法について、カスタムフックを中心に詳しく解説してきました。 スクロール位置は、ユーザーが今どこを見ているのかを把握するための非常に重要な情報です。 トップへ戻るボタンの表示制御、ヘッダーやフッターの表示切り替え、無限スクロールによるデータ取得など、 実務でも頻繁に利用される要素であり、Reactアプリの使いやすさや完成度に直結します。
スクロールイベントは一見すると簡単に扱えそうですが、実際にはイベント発火の頻度が高く、 そのまま実装するとパフォーマンス低下や不要な再レンダリングを引き起こす可能性があります。 そこでカスタムフックとして処理を切り出すことで、イベント管理・状態管理・後片付けまでを ひとまとめにし、安全で再利用しやすい設計が可能になります。
基本実装を振り返る
最も基本的な実装では、window.scrollY を使って現在の縦方向スクロール位置を取得し、
useState と useEffect を組み合わせて状態として管理しました。
このシンプルな構造だけでも、スクロール量に応じた表示制御が可能になります。
import { useState, useEffect } from "react";
function useScrollPosition() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY || window.pageYOffset);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return scrollY;
}
export default useScrollPosition;
このように、スクロール処理を一箇所に集約することで、コンポーネント側は 「値を受け取って使う」ことだけに集中でき、コードの見通しが大きく向上します。
パフォーマンスと実務を意識した設計
実際のアプリ開発では、デバウンスやスロットルといったパフォーマンス対策が欠かせません。 スクロールのたびに状態更新を行うのではなく、処理回数を制御することで、 なめらかなUIと安定した動作を両立できます。
また、SSR環境やモバイルブラウザでは window が存在しない、
もしくは挙動が異なる場合があるため、実行環境を意識した実装も重要です。
カスタムフックにまとめておけば、こうした対応も一箇所で管理でき、
後からの修正や拡張も容易になります。
React初心者にとっての学び
スクロール位置の管理を通して、Reactにおける「状態管理」「副作用処理」 「イベントリスナーの登録と解除」といった重要な概念を一度に学ぶことができます。 カスタムフックは最初は少し難しく感じるかもしれませんが、 一度理解すると、フォーム処理やAPI通信など、さまざまな場面で応用できる強力な武器になります。
生徒
「スクロール位置って、ただ値を取るだけだと思っていましたけど、 実はパフォーマンスや設計まで考える必要があるんですね。」
先生
「その通りです。特にスクロールイベントは回数が多いので、 雑に書くとアプリ全体が重くなってしまいます。 カスタムフックにまとめることで、そのリスクを減らせますよ。」
生徒
「useScrollPositionみたいに分けておくと、 トップへ戻るボタン以外にも使えそうですね。」
先生
「その気付きはとても大切です。 ヘッダーの表示制御や無限スクロール、アニメーション開始の判定など、 スクロール位置は応用範囲がとても広いんです。」
生徒
「まずはシンプルなカスタムフックから作って、 少しずつ機能を足していくのが良さそうですね。」
先生
「その考え方が正解です。 基本を押さえたうえで、自分のアプリに合った形に育てていきましょう。」