0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SSRとブラウザAPIの相性が悪い理由

Posted at

Next.jsなどのフレームワークを使用していると

「window is not defined」というエラーに出会ったり、
「Hydration failed because the server rendered HTML didn’t match the client」というエラーに出会うことが多いので、勉強も兼ねて記事にしてみました。

SSRとは

SSRは、サーバー上でHTMLを事前に生成し、クライアント(ブラウザ)に送信する仕組みです。

ReactなどのSPAでは、通常はクライアント側でJavascriptが実行されて初めて画面が描画されますが、SSRを使うとサーバーであらかじめHTMLが生成されるため、ブラウザが受け取った時点でページの構造が出来上がっており、SEO対策や表示速度の向上に繋がります。

ブラウザAPIとは

クライアント(ブラウザ)で動作するための特別なオブジェクトです。
これらはブラウザが持つ機能で、例えばwindow.innerWidthで画面幅を取得したり、document.getElementByIdでDOM要素を操作することができます。

なぜ両者の相性が悪いのか

SSRが動作するサーバー側は、Node.js環境で実行されているため、ブラウザの機能を持っていません。
そのため、SSR中にwindowやdocumentを参照すると、それらは存在しないためエラーになります。

Hidrationエラーについて

SSRによってサーバーから送られたHTMLは、あくまで静的なHTMLです。
これに対して、クライアント側でReactを起動し、イベントのバインドや状態管理を有効化するプロセスをハイドレーションと呼びます。

この時、サーバーで生成されたHTMLとクライアント側でReactが再構築するDOMが一致していない場合、
「Hydration failed because the server rendered HTML didn’t match the client.」というエラーや警告が発生します。
これは、サーバーとクライアントで描画結果が異なるときに起きる現象です。

解決方法

①クライアント専用の処理はuseEffect内に書く

SSR中にブラウザAPIを参照してしまうと、サーバー側にはそれらが存在しないためエラーになります。
そのため、クライアントでのみ動作させたい処理はuseEffect内に記述するのが基本的な対処法

useEffectはコンポーネントがマウントされたあとに実行されるため、サーバーでは実行されずにクライアント側でのみ実行されます。

②条件分岐でサーバーかクライアントかを判定する

もうひとつの解決方法は、サーバーかクライアントかを明示的に判定する方法です。
typeof window !== 'undefined'を使って、ブラウザ環境かどうかをチェックすることができます。

export default function Example() {
  const width = typeof window !== 'undefined' ? window.innerWidth : 0;

  return <p>画面幅は {width} px です</p>;
}

この場合、SSR中は0を返し、クライアント側ではwindow.innerWidth を返します。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?