【Next.js】useContextを使ってページのスクロール量を各コンポーネントで共通化するサンプルコード
概要
サイト内の各ページで使い回せるスクロール量を検出する関数(コンポーネント)の実装方法を解説します。
大まかな実装の流れは以下のようになります。
- スクロール量の値を格納するコンテキストを定義する(createContext)
- スクロール量を検知する関数を定義する(provider / useState / useEffect)
- 定義した関数をコンポーネントに組み込む(provider)
- スクロール量を取得して使用する(useContext)
propsを受け渡すことができるcontextを定義し、providerを経由し配下の子コンポーネントに渡す仕組みを構築します。
スクロール量を検出するコードはproviderの中にReact Hooks(useStateとuseEffect)で記述します。
開発環境
next 14.0.1
実装コード
1.スクロール量の値を格納するコンテキストを定義する(createContext)
変数ScrollContextをコンテキストとして定義します。
import { createContext } from 'react'
// 初期値0でコンテキストを作成
export const ScrollContext = createContext(0)
※コンテキストとは
通常、親コンポーネントから子コンポーネントには props を使って情報を渡します。しかし、props を多数の中間コンポーネントを経由して渡さないといけない場合や、アプリ内の多くのコンポーネントが同じ情報を必要とする場合、props の受け渡しは冗長で不便なものとなり得ます。コンテクスト (Context) を使用することで、親コンポーネントから props を明示的に渡さずとも、それ以下のツリー内の任意のコンポーネントが情報を受け取れるようにできます。
https://ja.react.dev/learn/passing-data-deeply-with-context
2.スクロール量を検知する関数を定義する(provider / useState / useEffect)
ブラウザのスクロール量を検知・取得するコードを以下のように作成します。
import React, { useState, useEffect } from 'react'
import { ScrollContext } from '@/components/scrollContext'
export const ScrollProvider = ({ children }) => {
// scrollYは、現在のスクロール位置を保持するための状態変数です。
const [scrollY, setScrollY] = useState(0)
// handleScrollはスクロールイベントが発生するたびに呼び出され、
// 現在のスクロール位置(window.scrollY)をscrollY状態にセットします。
const handleScroll = () => {
setScrollY(window.scrollY)
}
// useEffectフックを使用して、コンポーネントのマウント時にスクロールイベントリスナーを設定し、
// アンマウント時にはそれをクリーンアップ(削除)します。
// これにより、不要なイベントリスナーが残らず、パフォーマンスの問題を防ぎます。
useEffect(() => {
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
};
}, []);
// ScrollContext.Providerを使って、scrollY状態をコンテキストとして子コンポーネントに渡します。
// これにより、子コンポーネントはこのコンテキストを利用してスクロール位置にアクセスできます。
return (
<ScrollContext.Provider value={scrollY}>
{children}
</ScrollContext.Provider>
)
}
3.定義した関数をコンポーネントに組み込む(provider)
以下のように子コンポーネントをラップし、スクロール量の値を渡します。
import { ScrollProvider } from '@/components/scrollProvider'
export default function App({ Component, pageProps }) {
const getLayout = Component.getLayout || ((page) => page) //レイアウト用コンポーネント
return (
<ScrollProvider>
{getLayout(<Component {...pageProps} />)}
</ScrollProvider>
)
}
4.スクロール量を取得して使用する(useContext)
スクロール量の値を受け取り、描画ロジックのトリガーとして使います。
import React, {useState, useEffect, useContext} from 'react'
import { ScrollContext } from '@/components/scrollContext'
export default function Scroll() {
const scrollY = useContext(ScrollContext); // ScrollContextからスクロール量を取得
const isActive = scrollY > 100 // スクロール量が100pxを超えたらtrue
return (
<div className={`${isActive ? 'is-active' : ''}`}>
<button>btn</button>
</div>
)
}