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の型情報になります。
// ...
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
を使用してあげる方がよいのかなとは思うのですが、まあなんとなく論拠の薄い邪道感があるのでちょっと難しいところですね。