💡

[Next.js]サーバーコンポーネントだってトースターを出したい時がある

2023/11/30に公開

やりたいこと

例えば↓のようにリダイレクト時にユーザーにメッセージを伝えたい

import { redirect } from "next/navigation"

export const ServerComponent = () => {

  const session = getSession()
  if (session === null) {
    // 「ログインが必要です」というトースターを出力したい
    redirect("/login")
  }

  return <div>User only</div>
}
  • しかしトースターはクライアントサイドの動的な機能であり、サーバーサイドで直接出力することはできません
  • それでも出力させたいものは出力させたい、だから無理やりトースターを出力させました、というお話です。

結論

  • クライアントサイドにクエリパラメータを受け取るコンポーネントを作成し、?error_code=errorCodeを受け取った時に、トースターを出力するようにしました。
"use client";

import { useToast } from "@/components/ui/use-toast";
import { useRouter, useSearchParams } from "next/navigation";
import React, { useEffect } from "react";

const errorMessages = {
  loginRequired: "ログインが必要です",
  needAuth: "認証が必要です",
};

export const ErrorToaster = () => {
  const searchParams = useSearchParams();
  const errorCode = searchParams.get("error_code");
  const toaster = useToast();
  const router = useRouter();

  useEffect(() => {
    if (errorCode) {
      const errorMessage =
        errorMessages[errorCode as keyof typeof errorMessages] ||
        "エラーが発生しました";
      toaster.toast({
        variant: "destructive",
        title: errorMessage,
      });

      // URLからエラーコードを削除
      const newSearchParams = new URLSearchParams(window.location.search);
      newSearchParams.delete("error_code");
      router.replace(
        `${window.location.pathname}?${newSearchParams.toString()}`,
        { shallow: true }
      );
    }
  }, [errorCode, toaster.toast, router]);

  return null;
};

  • これをルートレイアウトに組み込みます
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Navigation />
        <Toaster />
        <ErrorToaster />
        {children}
      </body>
    </html>
  );
}
  • これでうまくいくはず・・・

結果

  • リダイレクトを次のようにします
import { redirect } from "next/navigation"

export const ServerComponent = () => {

  const session = getSession()
  if (session === null) {
    // リダイレクト時にクエリパラメータでエラーコードを指定する
    redirect("/login?error_code=loginRequired");
  }

  return <div>User only</div>
}
  • リダイレクトした時にトースターを出力させることができました

おしまい

Discussion