できない.dev

Next.js App Router で useState が使えないエラーが解消できない

App Router は既定でサーバーコンポーネント。
`useState` などのフックや `onClick` を使うファイルは先頭に `use client` ディレクティブが必要。
書き忘れ・位置ミス・境界設計の不一致が主因。

#nextjs#app-router#use-client#server-component#hooks

公開:

要約

App Router でフックが使えないエラーが出る場合、原因はほぼ「use client ディレクティブを書いていない / 位置が誤っている」。
サーバーコンポーネントが既定で、フックや onClick などのイベントハンドラはクライアント側でしか動かない。

よくある原因

  1. use client 書き忘れ: 「You're importing a component that needs useState. It only works in a Client Component.」エラーが出る
  2. ディレクティブの位置が誤り: import の下に書くと無効になり、ビルドエラーになる
  3. 境界設計のミス: 親をクライアント化して、本来サーバーで実行したい子コンポーネントまで巻き込んでいる
  4. クライアント専用ライブラリ: window などブラウザ API に依存する SDK をサーバー側で評価して落ちる

解決策

1. ディレクティブを正しく書く

公式の Client Components ドキュメント のとおり、ファイル先頭に書く。

"use client";
 
import { useState } from "react";
 
export function Counter() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>{n}</button>;
}

2. 境界を細かく分ける

page.tsx 自体はサーバーコンポーネントのままにし、インタラクション部分のみ別ファイル(例: Counter.tsx)に切り出して "use client"; を付ける。
データ取得はサーバーコンポーネント側のガイド に従いサーバー側で完結させると配信量も減る。

3. クライアント専用ライブラリを動的読み込み

import dynamic from "next/dynamic";
 
const Editor = dynamic(() => import("./Editor"), { ssr: false });

ブラウザ API に依存するライブラリはこれでサーバー側評価を回避できる。use client を付けたファイルから動的 import するのが定石。

この記事は役立ちましたか?