【Next.js】Vercelデプロイエラー「ReferenceError: localStorage is not defined」の原因と解決方法

エラー対処法のサムネイル Programming

勉強記録アプリをVercelでデプロイしようとGitHubと連携してインポートしたら、エラーが発生しました。DBも使わないし、簡単なコードで作ったアプリなのに「なんで?」という感じでかなり混乱しました。

エラー内容はこちらです。

ReferenceError: localStorage is not defined

今回は、実際に自分がハマった原因と解決方法を簡単にまとめてみます。

この記事でわかること
  • Vercelデプロイ時に localStorage is not defined エラーが発生する原因
  • なぜローカル環境では動くのにVercelではエラーになるのか
  • typeof window === "undefined" を使った対処方法
  • useEffect を使って安全に localStorage を扱う方法
  • localStorage エラーが発生した時の確認ポイント

発生した状況

ブログでも紹介していたNext.jsで勉強記録アプリをデプロイしようとしました。勉強時間を保存するために localStorage を使っていたのですが、ローカルでは問題なく動いていました。ですが、Vercelにデプロイするとエラーが発生。最初はVercelの設定ミスかと思ったのですが、原因は別のところにありました。

原因

Next.jsは、ブラウザ(CSR)だけでなくサーバー側(SSR/Build時)でもコードを実行します。 しかし、localStorage はブラウザの中にしか存在しない機能(Web API)です。

つまり、サーバー側でページを組み立てている段階で localStorage を読み込もうとして、「そんなもの知らないよ!」と ReferenceError: localStorage is not defined エラーを出してビルドにに失敗していました。

ヒョニ
ヒョニ

自分は最初、この「サーバー側でも実行される」という感覚があまり理解できておらず、「ブラウザで使えるなら全部使える」と思っていました(😂)

今回は3箇所で同じ問題が発生していました

① localStorageへ保存する関数がサーバーでも実行されていた

まず1つ目は、localStorage にデータを保存する関数です。最初は特に何も考えずそのまま書いていました。

ですが、Vercelではサーバー側でも実行される可能性があるため、ブラウザ環境かどうかを確認する必要がありました。

修正後はこちらです。

// この一行を先頭に入れることで、サーバー環境では処理をスキップさせます。
if (typeof window === "undefined") return;

最初にこれを追加することで、サーバー環境では処理を終了するようにしました。


② getStudyData関数でもサーバー実行を防ぐ必要があった

保存されたデータを取得する getStudyData() 関数も同様です。関数の中で localStorage を使用していたため、サーバー側で実行されるとエラーになります。

そのため、こちらも同じようにサーバーでは実行されないよう修正しました。

// サーバー環境なら空のオブジェクトを返して安全に終了
if (typeof window === "undefined") {return {} ;}

③ localStorageから直接データを取得していた

最後の問題は、コンポーネント内で直接 getStudyData() を実行していたことです。

最初はこのように書いていました。

//修正前
const studyData = getStudyData();

ですが、これだとVercelではgetStudyData() がサーバー側で実行される可能性があります。

そこで、初期値は空のStateにしておき、ブラウザ側に画面が描画された直後(マウント後)に実行される useEffect の中でデータを取得するように変更しました。

//修正後
const [studyData, setStudyData] = useState<StudyData>({});

useEffect(() => {
  // useEffectの中は必ずブラウザ側(Client)で実行されるため安全!
  const data = getStudyData();
  setStudyData(data);
}, []);

localStorage エラーが出たら確認するポイント

今回のエラーを解決する中で、確認したポイントをまとめてみました。

確認項目チェック内容
localStorageを直接使っていないかlocalStorage.getItem() や localStorage.setItem() をコンポーネントのレンダリング中に実行していないか確認する
windowチェックがあるかtypeof window !== "undefined" を使ってブラウザ環境か確認する
useEffectを使っているか初回データ取得は useEffect の中で行う
Stateで管理しているか取得したデータは state に保存して利用する
“use client” が付いているかClient Component として動作するように設定する

今回の対応フロー

今回、自分は次の流れで原因を特定して修正しました。

対応の流れ

localStorage is not defined エラー発生
↓
localStorage を使っている箇所を探す
↓
保存処理に window チェックを追加
↓
取得処理(getStudyData)にも window チェックを追加
↓
コンポーネント内の直接実行をやめる
↓
useEffect でデータ取得
↓
取得したデータを state で管理
↓
Vercelで再デプロイ
↓
解決!

もし Vercel や Next.js で localStorage is not defined エラーが発生した場合は、まず「サーバー側で localStorage を参照していないか?」を確認してみると解決のヒントになるかもしれません。

終わりに

今回のエラーを通して、

  • Next.jsではサーバー側でもコードが実行される
  • localStorage はブラウザ専用
  • データ保存だけでなく取得時も注意が必要
  • useEffect の実行タイミングが重要

ということを学びました。

最初はかなり混乱しましたが、実際に修正しながらSSRとCSRの違いを少し理解できた気がします。同じように localStorage is not defined エラーで困っている方の参考になれば嬉しいです。

関連記事

今回のエラーは、下記の勉強記録アプリをデプロイした際に発生したものです。アプリの実装内容について興味がある方は、こちらの記事もご覧ください。

【Next.js】Githubのような勉強記録アプリ①:簡単なTimer実装

 【Next.js】Githubのような勉強記録アプリ②:LocalStorageを活用したデータ保存

【Next.js】Githubのような勉強記録アプリ③:Gridを使って勉強時間を可視化する

【Next.js】Githubのような勉強記録アプリ④:LocalStorageに詳細を追加・変更しよう

Vercelデプロイ方法についてはこちらをご参考ください。(DBなし)

【Next.js】チケット販売システムを作る④ : Vercelを使ってデプロイしよう

タイトルとURLをコピーしました