💡
[Next.js]サーバーコンポーネントだってトースターを出したい時がある
やりたいこと
例えば↓のようにリダイレクト時にユーザーにメッセージを伝えたい
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