IT-Q

localStorage に保存したテーマ(ライト/ダーク)を初期表示から反映したい。どう実装しますか。

localStorage はブラウザにしかないため、そのままではサーバーのHTMLに正しい値を入れられません。Cookie に持たせてサーバーで読むか、描画前に適用する形へ変えます。

面接官が見ているのはハイドレーションの制約を踏まえて設計できるか。定番の詰みどころ。

これは「「Hydration failed」というエラーが出ました。何が起きていますか。」への追撃質問です。

よくある答えと、面談でどう見られるか

  • Cookie に移す。サーバーで読めるので初期HTMLに正しい値を入れられる
    筋の良い解です。ズレが原理的に起きなくなります。ただし cookies() を読むとルートが動的になるので、そこは意識しておきたいところです。
  • useEffect で読んで後から反映する
    実装は簡単で、間違いではありません。ただし初回に既定テーマが一瞬見えるちらつきが出ます。面談では「その代償を認識しているか」まで聞かれます。「ちらつきは出るが許容範囲と判断した」と言えれば通ります。
  • コンポーネントの先頭で localStorage を読んで初期 state にする
    サーバーには localStorage が無いので、そこで落ちます。仮に回避してもサーバーとクライアントで値が違うため、まさにハイドレーションエラーの原因になります。
  • そのコンポーネントだけ SSR を無効にして、クライアントでのみ描画する
    手としては成立しますし、実務でも使われます。ただしその部分は初期HTMLに入らないので、結局ちらつきや領域の抜けが出ます。「なぜサーバーで描けないのか」に答えられないと、逃げに見えます。

面接官は何を見ているか

  • サーバーは localStorage を読めない、と前提を押さえた
  • 初期描画を合わせる方法(Cookie など)を提案できた
  • useEffect で後から当てる案の代償(ちらつき)に触れた

模範解答

まず前提を確認します。localStorage はブラウザにしかないので、サーバーは値を知りません。したがって「サーバーの HTML に正しいテーマを入れる」ことはlocalStorage のままでは原理的にできません。

素直な解は Cookie に移すことです。Cookie はリクエストと一緒に送られるので、サーバー側で cookies() から読んで、最初の HTML に正しいテーマを入れられます。ズレが起きないのでハイドレーションエラーも出ません。ただし cookies() を読むとそのルートが動的レンダリングになるので、静的にしたいページでは Suspense で切り出すなどの検討が要ります。

localStorage のままでいくなら、useEffect で読んで後から当てる形になります。実装は簡単ですが、初回に既定テーマが一瞬見えてから切り替わる、いわゆるちらつきが出ます。これを避けるために、head で同期的にクラスを付ける小さなスクリプトを挟む方法もあります。

面談では「どれが正解か」より、「サーバーが知り得ない情報を初期HTMLに入れることはできない」と言えるかを見られます。そこを押さえていれば、選んだ方法がどれでも説明が通ります。