Next.jsでウィンドウオブジェクトにアクセスする方法 | 初心者向け解説

Next.jsでウィンドウオブジェクトにアクセスする方法 | 初心者向け解説

本日はNext.jsでウィンドウオブジェクトにアクセスするいくつかの方法を初心者向けに解説していきます。

Next.jsを使用するときに遭遇する可能性のある非常に一般的なエラーは、ウィンドウオブジェクトにアクセスした際に返されるエラーです。

Next.jsではWindowは定義されておりません。

Next.jsアプリで HTML DOMからウィンドウオブジェクトにアクセスしようとすると以下のエラーがスローされます。

Unhandled Rejection (ReferenceError): window is not defined

「Windowが定義されていません」と返ってきます。

ターミナル自体にエラーが発生します、つまりコンパイルエラーとなります。

window is not definedこのエラーは、サーバー側のコードでウィンドウオブジェクトにアクセスしようとすると発生します。

このエラーは通常、Next.jsおよびNode.jsのアプリケーションで発生しNode.jsサーバーがブラウザ環境を提供していないためとなります。

つまり、Next.jsでは、ウィンドウオブジェクトがクライアント側でのみ利用可能であという制限があります。

NextJSでWindowが定義されていないのはなぜですか?

Next.jsのドキュメントによると、以下のように記載されております。

Next.jsは、本番環境に必要なすべての機能 (ハイブリッド静的レンダリングとサーバー レンダリング、TypeScript サポート、スマートバンドル、ルートプリフェッチなど) を備えた最高の開発者エクスペリエンスを提供します。

最も重要なのは、サーバーサイドレンダリングSSR)です。

ウィンドウオブジェクトは、ブラウザで開いているウィンドウへの参照です。

したがって、ウィンドウオブジェクトはクライアント側でのみ使用できます。

Next.jsはNode.jsのランタイムでコンパイル・ビルドされており、Node.jsはウィンドウオブジェクトを持ちません。

では、なぜNode.jsで動作させるのか?

Next.jsは、Node.js上で動作するように設計されています。

Node.jsは、JavaScriptをサーバーサイドで実行するためのプラットフォームであり、高速でスケーラブルなWebアプリケーションを構築するために広く使用されています。

Next.jsは、Node.jsの機能を活用して、サーバーサイドでReactコンポーネントレンダリングし、HTMLを生成することができます。

また、Node.jsのパッケージマネージャーであるnpmを使用して、依存関係の管理やビルドプロセスの自動化を行うことができます。

これにより、開発者は簡単にNext.jsアプリケーションを構築し、デプロイすることができます。

Next.jsがサーバーで実行されるウィンドウオブジェクトがページに含まれている場合、その問題が発生する可能性があり、その環境では、windowにアクセスする事は不可能となります。

総じて、Next.jsは、SSRをサポートするために、ウィンドウオブジェクトを制限しています。

これは、Reactのコンポーネントがサーバー側でレンダリングされるため、ブラウザのウィンドウオブジェクトにアクセスできないためです。

Next.jsで"window is not defined"を回避するには?

このエラーを回避する方法がいくつかございます。

まず、先述で解説した通り、Next.jsでは、サーバーサイドレンダリングSSR)を有効にすると、コンポントがサーバー側でレンダリングされるため、ブラウザのウィンドウオブジェクトにアクセスできせん。

そのため、ウィンドウオブジェクトにアクセスする必要がある場合は、「SSR」を無効にする必要があります。

Next.jsアプリケーションでウィンドウオブジェクトにアクセスするクールな方法をいくつか紹介しましょう。

ウィンドウ オブジェクトが定義されているかどうかを型で確認する

React useEffectフック内でwindowオブジェクトを使用するか、またクラスコンポーネントではcomponentDidMountライフサイクルメソッドを使用します。

globalThisプロパティを使用してウィンドウにアクセスする

Next.jsが提供する組み込み関数を使用する

useEffectフックを使用する

useEffect内のウィンドウオブジェクトをいじっても問題はありませんので以下のメソッド内のコードで常に使用できます。

const [width, setWidth] = useState(0);  
useEffect(() => {
    setWidth(window.innerWidth);  
},[]);

なぜ、useEffectフックでは使用できるのか?

それは useEffectフックは、サーバーサイドでは実行されないためです。

ReactのuseEffect フックを使用すると、初回レンダリング後にuseEffectが実行されるので、コードはアプリケーションのクライアントサイドでのみ実行されます。

もちろん上記のように、useEffect単独で使用しても構いませんがNext.js外部のブラウザ(DOMノードなど)でオブジェクトを取得し、その中に何かをレンダリングする必要がある場合はuseEffectおよびuseRefフックと組み合わせることもできます。

import { useRef, useEffect } from 'react'

const Page = () = > {
const ref = useRef()

useEffect(() => {
ReactDOM.render(<Test /> ref.current);
  }, [])

return <div ref={ref}/>
}

Dynamic import(動的インポート)

コンポーネント内の複数の場所で、ウィンドウオブジェクトにアクセスしていて、以下のメソッドを常に使用することはあまり現実的ではないと仮定します。

const Home = ()  => {

useEffect(() => {
 window.Message = "Welcome!"
  }, [])

return <div>Hello</div>
}

コンポーネント全体をクライアント側でのみ実行したい場合は 、Next.jsの動的インポート機能を使用できます。

Next.jsにはDynamic importがあります。

ブラウザでのみ動作するライブラリを含むモジュールです。

import dynamic from "next/dynamic"

const TestComponent = dynamic(() => 
import("../components/TestComponent"), {
 //サーバーサイド側でインポートはしない
ssr: false, // SSRの無効
})

const Home = ()  => {

useEffect(() => {
 window.Message = "Welcome!"
  }, [])

return 
<div>
<TestComponent />
</div>
}

上記のように、TestComponentを動的にインポートすることができるようになり、コードを実行してもエラーがスローされる事はありません。

Dynamic importを使用することをお勧め致します。

型のチェック

Reactの外部でも役立つ別のオプションがあります。

現在のコンテキストに、ウィンドウオブジェクトが定義されているかどうかをチェックするだけで、コンポーネントSSRを無効にする方法となります。

const [width, setWidth] = useState(0);
if( typeof window !== undefined) {
     setWidth(window.innerWidth)
}

// または

if( typeof window === "object") {
     setWidth(window.innerWidth)
}

このアプローチは最も単純で簡単となります。

必要なコードを実行する前に、ウィンドウオブジェクトの型を確認するだけです。

上記のようにすれば、Windowグローバル変数へのアクセスを提供します。

この方法で問題は解決します、ブラウザ環境では条件内でしか実行しないからです。

globalThisプロパティを使用する

globalThisプロパティは、環境全体でグローバルとしてthis参照へのアクセスを提供してくれます。

グローバルオブジェクトは、コード内のどこからでもアクセスできるJavaScriptの特別なオブジェクトであり、ブラウザではウィンドウオブジェクト、Node.js環境ではglobalオブジェクトとして知られています。

const Home = ({data}) => {

console.log(globalThis.window?.innerWidth);

return <>{data.post.text}</>

}

これはthisに似たグローバル値となっています。

thisなどの同様のプロパティとは異なり、ウィンドウ コンテキストと非ウィンドウコンテキストで動作することが保証されております。

globalThisプロパティは、ES2020で導入され、ES2021以降のすべての環境で利用可能となっております。

Next.jsでは、SSRを無効にする方法として、getInitialPropsメソッドを使用することができます。

組み込み関数でウィンドウオブジェクトにアクセスする

getInitialPropsメソッドは、コンポーネントレンダリングされる前に実行され、クライアント側でのみ実行されます。

このため、getInitialPropsメソッド内でウィンドウオブジェクトにアクセスすることができます。

しかし、getInitialPropsメソッドは、「Next.js v9.3」以降では非推奨となっており、代わりにgetStaticPropsまたはgetServerSidePropsを使用することが推奨されています。

getStaticPropsまたはgetServerSidePropsを使用して、SSRを無効にし、ウィンドウオブジェクトにアクセスする例を以下に示します。

import React from 'react';

// Reactコンポーネント
const MyComponent = ({ windowWidth }) => {
  return (
    <div>
      <p>Window width: {windowWidth}px</p>
    </div>
  );
};

// サーバーサイドで実行される関数
export async function getServerSideProps() {
  // サーバーサイドで実行されているかどうかを確認
  const isServer = typeof window === 'undefined';
  let windowWidth = 0;
  if (!isServer) {
    // クライアント側で実行されている場合は、ウィンドウ幅を取得
    windowWidth = window.innerWidth;
  }
  // propsとしてウィンドウ幅を返す
  return {
    props: {
      windowWidth,
    },
  };
}

export default MyComponent;

上記の例では、getServerSidePropsメソッド内で、typeof window === 'undefined'を使用して、サーバーサイドで実行されているかどうかを確認しています。

サーバーサイド実行されている場合は、window.innerWidthにアクセスできないため、windowWidth0に設定します。

クライアント側で実行されている場合は、window.innerWidthにアクセスして、windowWidthに値を設定します。

getStaticPropsメソッドを使用する場合も同様に、typeof window === 'undefined'を使用して、サーバーサイドで実行されているかどうかを確認し、ウィンドウオブジェクトにアクセスします。

注意点として、getStaticPropsまたはgetServerSidePropsメソッドは、「Next.js v9.3」以降でのみ使用できます。

また、getStaticPropsメソッドは、ビルド時に実行されるため、データが頻繁に変更される場合は使用しない方が良いです。

一方、getServerSidePropsメソッドは、リクエストごとに実行されるため、データが頻繁に変更される場合でも使用することができます。

最後に

Next.jsでウィンドウオブジェクトにアクセスする方法は、Reactアプリケーションの開発において非常に重要です。

ウィンドウオブジェクトにアクセスすることで、ブラウザのウィンドウサイズやスクロール位置などの情報を取得することができます。

また、window.addEventListenerを使用して、ウィンドウのリサイズやスクロールなどのイベントを監視することができます。

これにより、ウィンドウの変更に応じてコンポーネントを再レンダリングすることができます。

ですが、初めてNext.jsを使用する際に遭遇する可能性のあるエラーである"ReferenceError: window is not defined"について心配する必要はありません。

Next.jsでは、コードがクライアントとサーバーの両方で実行されることを念頭に置く必要があります。

この記事では、Next.jsでウィンドウオブジェクトにアクセスするためのいくつかの方法を初心者向けに紹介しました。

プロジェクトの要件に基づいて、どの方法が最適かを決定する必要があります。

また、SSRを無効にするかどうかについても、慎重に考慮する必要があります。

本日は以上となります。

最後までこの記事を読んで頂きありがとうございます。

この記事が役に立ったら、ブックマークと共有していただけると幸いです。

プライバシーポリシー

© 2023 Icons8 LLC. All rights reserved.

© [deve.K], 2023. React logo is a trademark of Facebook, Inc. JavaScript is a trademark of Oracle Corporation and/or its affiliates. jQuery and the jQuery logo are trademarks of the JS Foundation. TypeScript and the TypeScript logo are trademarks of the Microsoft Corporation. Next.js and the Next.js logo are trademarks of Vercel, Inc. Firebase and the Firebase logo are trademarks of Google LLC. All logos edited by [deve.K].