SSR環境における `useEffect` の深淵:ハイドレーションの「壁」をどう乗り越えるか
やあ。Reactの海を渡り歩いている諸君、今日もコンポーネントのライフサイクルと格闘しているかな?
Reactを触り始めてしばらく経つと、必ずと言っていいほど直面する「SSR(Server Side Rendering)と `useEffect` の不協和音」。特にNext.jsなどで開発していると、「`window` is not defined」という懐かしいエラーや、サーバーとクライアントで描画内容がズレる「ハイドレーション・ミスマッチ」に頭を抱えた経験があるはずだ。
今日は、なぜSSR環境で `useEffect` が「沈黙」するのか、そしてハイドレーション後にどう振る舞うのか、その裏側にある泥臭い現実と実戦的な解決策を叩き込んでいく。
—
なぜSSRで `useEffect` は実行されないのか?
結論から言おう。`useEffect` はブラウザのメモリ上で動く「副作用」のフックだからだ。
SSRのプロセスを思い出してほしい。サーバー側でReactが行っているのは、コンポーネントツリーをHTML文字列(あるいはストリーム)に変換する「レンダリング」という作業に過ぎない。サーバーはブラウザのようなDOM環境を持たないし、ましてや `componentDidMount` や `useEffect` のような「ブラウザが描画を終えた後の処理」を実行する責任も権限もない。
サーバー側で `useEffect` を実行してしまうと、以下のような地獄が待っている。
1. `window` や `localStorage` にアクセスしようとしてサーバーがクラッシュする。
2. データのフェッチがサーバーとクライアントで二重に走り、パフォーマンスを食いつぶす。
だからこそ、Reactは仕様として「SSR時には `useEffect` を完全に無視する」という選択をしているんだ。賢明な判断だと思わないか?
—
ハイドレーションという「架け橋」の正体
サーバーから送られてきた静的なHTMLを、ブラウザが「生きたインタラクティブなUI」へと昇華させるプロセス。これがハイドレーションだ。
ハイドレーションにおいて、Reactはサーバーから届いたHTMLの構造と、手元にあるコンポーネントツリーを照らし合わせる。ここで重要なのは、ハイドレーションが完了し、ブラウザ上でReactの管理下に入った瞬間に初めて `useEffect` が火を吹くという点だ。
この「サーバーから送られたHTML(静的)」と「JSが読み込まれた後のUI(動的)」のギャップをどう埋めるかが、シニアへの登竜門となる。
—
実践:SSR/CSRのズレを安全に制御するパターン
現場でよく使う、安全かつ堅牢な「ハイドレーション待ち」のパターンを伝授しよう。ポイントは、Reactの `useState` と `useEffect` を組み合わせて「今がクライアント側か?」を判定することだ。
import { useState, useEffect } from ‘react’;
/
- SSR環境でのハイドレーション不一致を防ぐためのカスタムフック
- サーバー側とクライアント側でレンダリング結果を変えたい場合に重宝する。
/
export const useIsMounted = () => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
// この中身はクライアントでのみ実行される
setIsMounted(true);
}, []);
return isMounted;
};
// 実際のコンポーネントでの使用例
export const MyComponent = () => {
const isMounted = useIsMounted();
// ハイドレーションが終わるまではサーバーと同じ(あるいは何も表示しない)
if (!isMounted) {
return
;
}
// ハイドレーション後のみ、ブラウザ特有のAPIを安全に叩ける
return (
);
};
このコードがなぜ「美しい」のか?
1. 初期レンダリングの整合性: サーバー側では `isMounted` が常に `false` なので、常に同じHTMLを生成できる。これでハイドレーション・ミスマッチは起きない。
2. 副作用の遅延: クライアントにJSが渡り、ハイドレーションが完了した瞬間に `useEffect` が走り、`isMounted` が `true` になる。これによって、再レンダリングが走り、ブラウザ特有のUIが正しく表示されるというわけだ。
—
最後に:シニアアーキテクトからの助言
「`useEffect` を使わずに済むなら、それに越したことはない」
これが私の信条だ。SSR環境では、そもそも `useEffect` に依存しなくても済むようなデータの持ち方(例えばNext.jsの `getServerSideProps` や `Server Components` でのデータ取得)を優先すべきだ。
`useEffect` はあくまで、ブラウザのDOMに触れる必要があるときや、外部ライブラリの初期化など、どうしても「クライアント側の実行」が必要な場合のみに使う奥の手だと思っておいてくれ。
SSRとクライアントサイドの境界線。ここを理解すれば、諸君が書くコードの質は一段上のレベルに到達するはずだ。明日からの開発で、ぜひ意識してみてほしい。
また何か壁にぶつかったら、いつでも聞いてくれ。現場からは以上だ。

コメント