LoginSignup
5
0

More than 1 year has passed since last update.

Next.jsで必須のライブラリでwindow is not definedを引いたとき

Posted at

vonageのVideo APIを触っているさなか、久々に ReferenceError: window is not definedを引いて対応に手古摺ったのでその対処法を記します。

そもそもwindow is not definedってどんな時に出るエラーなのか

windowを使ってるけどwindowがない環境で処理が実行されている、つまりブラウザ環境でしか動かない処理をサーバーサイド側で動かしたときに生じるエラーの一つです。

対処法

大きく分けて二つ存在します。

useEffectを使用する

直感的に対応した場合、おそらくこちらになると思います。
useEffectは当然サーバーサイド側で実行されないので、この中でimportをすればいいという発想です。

例のごとく、useEffect(()=>{}, [])を作成し、その中で動的読み込みをすることで解決します。

const Home: NextPage = () => {
  useEffect(() => {
    (async () => {
      const Foo = (await import("package")).default;
      // named exportの場合
      const Bar = (await import("package").then((module) => module.Bar));
    })
  }
  return <p>hello world</p>
}

こちらわかりやすくていいんですが、元々別の処理のためにuseEffectを使っているところに付け足すとかならともかく、そうでない場合はwindow is not defined解決のためにuseRffectして中で即時実行関数を非同期で宣言して~なんて面倒ですし可読性が下がります。

いやまあ正攻法的には本当にこっちだと思うんですけどね。
思うんですが、あまりしたくない手段です。

next/dynamicを使用する

なんとなく邪道感ありますが、個人的にはこちらを推したいです。

next/dynamicは、Next.js環境下における動的読み込み用のライブラリです。
名前からわかる通りNext.jsに付属しているので、追加のインストールなしで使用できます。

使い方としてはこんな感じです。

import dynamic from "next/dynamic";

const foo = dynamic(() => import("components/package"), {
  ssr: false,
});

const Home: NextPage = () => { // ...

で、一見簡単に見えるこれの何に邪道感があるのかというと、これの返り値が問題なのです。

以下が、dynamicの型情報になります。

dynamic.d.ts
// ...
export default function dynamic<P = {}>(dynamicOptions: DynamicOptions<P> | Loader<P>, options?: DynamicOptions<P>): React.ComponentType<P>;
// ...

見ての通り、返り値がComponentTypeとなっています。
念のためもっと参照をたどると、中身は ComponentClass<P> | FunctionComponent<P>;

そうです。
返り値にコンポーネントしか対応していないので、ライブラリのためだけにコンポーネントを一つ増やす必要があります。

いやまだそれだけなら特に問題はないですが、問題はフラグメントだけのコンポーネントを返すことになりうるということです。

なぜなら、まあ処理をうまいこと切り出してそのコンポーネント内だけの話に済ませられれば良いのですが、window is not definedなんてグローバルオブジェクト不足のエラー吐いているライブラリがそう都合よくいくわけがないからです。

というわけで、こんな感じにフラグメントのみを返す違和感のある処理を書くことになります。

import { FC } from "react";
import foo from "package"

export const Foo: FC = () => {
  // fooを使った何かしらの処理
  return <></>;
}
export default Foo

うーん、ルール上許されているとはいえ違和感。
でも、useEffectと非同期の即時実行関数を増やすよりかは可読性的に良いのも確か。

おわりに

というわけで、window is not definedを引いた時の対処法でした。
個人的には、基本的にnext/dynamicを使用してあげる方がよいのかなとは思うのですが、まあなんとなく論拠の薄い邪道感があるのでちょっと難しいところですね。

5
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0