【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をコピーしました