🫥

[Next.js]Event handlers cannot be passed to Client Component propsエラー

2024/11/26に公開

Next.js アプリケーションを開発しているとき、以下のようなエラーに直面したことはありませんか?

Error: Event handlers cannot be passed to Client Component props.
<button onClick={function onClick} className=... children=...>
                  ^^^^^^^^^^^^^^^^^^
If you need interactivity, consider converting part of this to a Client Component.

これは、Server Components がクライアントサイドのイベントハンドラ(例: onClick)をサポートしていないために発生します。onClick のようなクライアントサイドの機能を使用するには、明示的にクライアントコンポーネントにする必要があります。


修正前のコード

以下のコードは、Header コンポーネントに onClick イベントハンドラを含むボタンを含んでいます。しかし、このコンポーネントは Server Component として処理されるため、エラーが発生します。

export default function Header() {
  const scrollToSection = (id: string) => {
    const section = document.getElementById(id);
    if (section) {
      section.scrollIntoView({ behavior: 'smooth' });
    }
  };

  return (
    <header>
      <button onClick={() => scrollToSection('features')}>講座内容</button>
    </header>
  );
}

修正方法

エラーを解決するには、クライアントサイドのロジックを切り離して専用のクライアントコンポーネントを作成することが推奨されます。これにより、サーバーコンポーネントとクライアントコンポーネントの役割を明確に分けることができます。

手順: クライアント専用部分を分離

  1. クライアント専用のコンポーネントを作成
    • クライアントサイドのロジック(例: onClick イベントハンドラ)を新しいコンポーネントに移動します。
// ClientNav.tsx
"use client";

export default function ClientNav() {
  const scrollToSection = (id: string) => {
    const section = document.getElementById(id);
    if (section) {
      section.scrollIntoView({ behavior: "smooth" });
    }
  };

  return (
    <nav>
      <ul>
        <li>
          <button onClick={() => scrollToSection("features")}>
            講座内容
          </button>
        </li>
      </ul>
    </nav>
  );
}
  1. Header コンポーネントでクライアントコンポーネントを利用
    • クライアント専用のコンポーネントをインポートして利用します。
// Header.tsx
import ClientNav from "./ClientNav";

export default function Header() {
  return (
    <header>
      <h1>アプリ名</h1>
      <ClientNav />
    </header>
  );
}

なぜこの方法が必要か

Next.js の Server Components では、以下の理由でクライアントサイドのイベントハンドラがサポートされません。

  1. サーバー側での効率的なレンダリング

    • Server Components は、HTML を事前生成するための仕組みで、クライアントサイドの JavaScript が不要な部分を効率的に処理します。
  2. JavaScriptの削減

    • 必要最小限のクライアントサイドスクリプトのみを読み込むことで、アプリのパフォーマンスを向上させます。
  3. 役割の分離

    • サーバーサイドで行うべき処理(例: データ取得)と、クライアントサイドで行うべき処理(例: インタラクション)を明確に分けることで、コードの保守性を向上させます。

修正後のコード

最終的なコードは以下のようになります。サーバーコンポーネント (Header) とクライアントコンポーネント (ClientNav) の役割が明確に分離されています。

ClientNav.tsx

"use client";

export default function ClientNav() {
  const scrollToSection = (id: string) => {
    const section = document.getElementById(id);
    if (section) {
      section.scrollIntoView({ behavior: "smooth" });
    }
  };

  return (
    <nav>
      <ul>
        <li>
          <button onClick={() => scrollToSection("features")}>
            講座内容
          </button>
        </li>
      </ul>
    </nav>
  );
}

Header.tsx

import ClientNav from "./ClientNav";

export default function Header() {
  return (
    <header>
      <h1>アプリ名</h1>
      <ClientNav />
    </header>
  );
}

まとめ

  • エラーの原因: Server Components ではクライアントサイドのイベントハンドラ(例: onClick)を直接使用できない。
  • 解決策: クライアントサイドのロジックを専用コンポーネントに分離し、"use client" を利用する。
  • 利点: サーバーコンポーネントの効率性を維持しつつ、必要な部分だけクライアントサイドで処理できる。

この方法を利用することで、Next.js アプリケーションのパフォーマンスと保守性を向上させることができます。

Discussion