🐢
Next.jsでフラッシュメッセージを実装する
これは何?
- タイトルの通り。サーバーアクションの結果をフロントに伝達するためにフラッシュメッセージを実装したときのメモ
- 環境
Next.js 15.2.0 App Router
shadcn/ui
- 以前の記事をブラッシュアップして、前回より汎用的に使えるようにしました。
ユースケース
Ruby on Rails
と同じ要領で、サーバー側のアクション(今回はReact Server Action
)が実行されたら、期限0秒のcookie
を設定し、フロント側にトーストメッセージを出す。
動作デモ
コード設計
レイアウトに配置するコンポーネント
flash-toaster.tsx
import { Toaster } from "@/components/ui/sonner";
import { cookies } from "next/headers";
import { FlashToasterClient } from "./flash-toaster/flash-toaster-client";
export type Flash = {
type: "success" | "error";
message: string;
};
export async function setFlash(flash: Flash) {
(await cookies()).set("flash", JSON.stringify(flash), {
path: "/",
maxAge: 0,
});
}
export default async function FlashToaster() {
const flash = (await cookies()).get("flash");
return (
<FlashToasterClient flash={flash?.value}>
<Toaster />
</FlashToasterClient>
);
}
-
maxAge: 0
にすることで、すぐ期限切れになるようcookie
を設定 -
FlashToasterClient
はuse client
を付けてクライアントコンポーネントにする。cookie
の値は非同期で取得するため、params
として渡す。 - クライアントコンポーネントの
children
にToaster
を渡すことで、children
をサーバーコンポーネントとして定義する(参考) ※ レンダリングパフォーマンス向上のためと、フラグメントを使いたくなかったため。
フラッシュの表示を担うコンポーネント
flash-toaster-client.tsx
"use client";
import { ReactNode, useEffect } from "react";
import { toast } from "sonner";
import { Flash } from "@/lib/flash-toaster";
export function FlashToasterClient({
flash,
children,
}: {
flash: string | undefined;
children: ReactNode;
}) {
useEffect(() => {
if (!!flash) {
const data: Flash = JSON.parse(flash);
switch (data.type) {
case "success":
toast.success(data.message);
break;
case "error":
toast.error(data.message);
break;
default:
break;
}
}
});
return children;
}
-
Flash
型をJSON.parse()
の結果に指定してany
を回避
レイアウトファイルに配置
layout.tsx
import "@/app/ui/global.css";
import { inter } from "@/app/ui/fonts";
import FlashToaster from "@/lib/flash-toaster";
import { ReactNode } from "react";
export default async function RootLayout({
children,
}: {
children: ReactNode;
}) {
return (
<html lang="en">
<body className={`${inter.className} antialiased`}>
{children}
<FlashToaster />
</body>
</html>
);
}
- メインのコンポーネントをレイアウトに配置します。
フラッシュの設定
actions.ts
"use server";
import { setFlash } from "@/lib/flash-toaster";
export async function deleteInvoice(id: string, _prevState: unknown) {
await prisma.invoices.delete({
where: { id: id },
});
await setFlash({ type: "success", message: "delete invoice successful." });
revalidatePath("/dashboard/invoices");
}
- サーバーアクションの実行後に表示したいフラッシュメッセージの
type
とmessage
を指定します。
感想
こちらの記事を参考に実装しました。無理な実装にならずに出来てよかったです。
Discussion