📱
React/Next.js環境での効率的なウィンドウリサイズ検知
ウェブサイトやアプリケーションでスマートフォン幅では特定のアニメーションを非表示にするなど、画面サイズに応じて振る舞いを変更したい場合があります。ReactやNext.jsではhooksを活用することで、VanillaJSとは異なるアプローチでこうした制御を効率的に行うことができます。
VanillaJSとReact/Next.jsでの制御方法
両者のコードを比較
VanillaJs
const animationController = {
// 初期化関数
init() {
this.updateAnimation();
window.addEventListener("resize", this.updateAnimation);
},
// アニメーション更新関数
updateAnimation() {
const windowWidth = window.innerWidth;
if (windowWidth < 768) {
// モバイル向けのアニメーション
} else {
// デスクトップ向けのアニメーション
}
},
// 明示的なクリーンアップ関数
destroy() {
window.removeEventListener("resize", this.updateAnimation);
}
};
document.addEventListener("DOMContentLoaded", () => {
animationController.init();
// ⚠️重要: ページ遷移や要素削除時にdestroy()を呼び出す必要がある
// 何らかのタイミングで: animationController.destroy();
});
React/Next.js
const [isMobile, setIsMobile] = useState<boolean>(false);
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768);
};
handleResize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div> {isMobile ?
( /** モバイル幅での処理 */ ) : ( /** PC幅での処理 */ );
両者を比較すると、Reactのフックを活用したアプローチには以下のメリットがあります:
- コードの見通しがよい - 関連する処理が近くにまとまっており、処理の流れが把握しやすい
- 自動クリーンアップ - コンポーネントのアンマウント時に自動的にクリーンアップされるため、リソースリークを防ぎやすい
-
ステート管理との連携 - ブラウザ幅の変化を
isMobile
というステートで管理することで、JSXでの条件分岐が簡単になる
GSAP活用した例(React/Next.js)
const sampleRef = useRef<HTMLDivElement>(null);
useGSAP(() => {
// ScrollTriggerを制御
ScrollTrigger.getAll().forEach(st => {
if (st.vars.trigger === sampleRef.current) {
st.kill();
}
});
if (!isMobile && sampleRef.current) {
// PCでのアニメーション
gsap.fromTo(
sampleRef.current, {
...
},
{
...
scrollTrigger: {
...
},
}
);
}
// スマホでのアニメーションまたは状態の初期化
else if (isMobile && sampleRef.current) {
gsap.to(
sampleRef.current, {
...
}
);
}
}, [isMobile]); // isMobileの変更を監視
return (
<>
<div ref={sampleRef}></div>
</>
)
少し補足ですが、
-
ScrollTrigger.getAll().forEach(...)
は、sampleRef.currentをトリガーとして使用している既存のすべてのScrollTriggerインスタンスをクリーンアップするために書かれています。 - その後、条件に応じて(isMobileの値に基づいて)新しいScrollTriggerを作成しています。
クリーンアップ関数の重要性
クリーンアップ関数を実装しないと、以下の問題が発生する可能性があります:
- メモリリーク - イベントリスナーが蓄積され、メモリ使用量が増加
- パフォーマンス低下 - 不要になったコンポーネントのリスナーも実行され続ける
- 予期しない動作 - 古いコンポーネントのロジックが新しいUIに影響を与える
注意点:Next.jsでupdateAnimation関数をそのまま使用する場合の問題
Next.js(React)環境でupdateAnimation関数をそのまま使用すると、以下の問題が発生します:
-
SSRエラー - サーバーサイドレンダリング時に
window
やdocument
などのブラウザAPIが存在しないため、window is not defined
などのエラーが発生 - メモリリーク - コンポーネントのライフサイクルと連携していないため、コンポーネントがアンマウントされてもイベントリスナーが残り続ける
- 予期しない再レンダリング - Reactの再レンダリングのたびにリスナーが重複して登録される可能性がある
こうした理由から、React環境では必ずuseEffectなどのフックを活用してブラウザAPIにアクセスし、適切なタイミングでクリーンアップを行うことが推奨されています。
まとめ:React/Next.js環境での効率的なウィンドウリサイズ対応のベストプラクティス
-
React/Next.jsではフックを活用する
- VanillaJSのようなグローバルな関数ではなく、コンポーネント内のuseEffectを使用する
- コンポーネントのライフサイクルに合わせてリソースを管理する
-
stateでブラウザ幅の状態を管理する
-
isMobile
のようなステートを使って、画面幅の変化を管理する - これによりJSXでの条件分岐が簡単になる
-
-
自動クリーンアップの利点を活かす
- useEffectの戻り値でクリーンアップ関数を実装し、自動的なリソース解放を行う
- VanillaJSのように明示的なdestroy関数の呼び出し忘れによるメモリリークを防止する
-
SSRの問題に注意する
- Next.jsのSSR環境では、ブラウザAPI(window, document)へのアクセスをuseEffect内に限定する
React/Next.jsでのウィンドウリサイズ検知は、フックの活用により簡潔かつ安全に実装できます。特にuseEffectとuseStateの組み合わせにより、コンポーネントのライフサイクルと連携した効率的なコードが書けるようになり、メモリリークなどの問題を自動的に防止できることが大きなメリットです。
Discussion