文章
nextjs使用shadcn ui前后端交互demo
/app/api/comments/route.ts
import {NextResponse} from "next/server";
// 模拟数据
let comments = [
{ id: 1, text: '这是第一条评论', author: '用户A' },
{ id: 2, text: '这是第二条评论', author: '用户B' },
]
export async function GET() {
return NextResponse.json(comments);
}
export async function POST(request: Request) {
const {text} = await request.json()
// 模拟存储
const newComment = {
id: comments.length + 1,
text,
author: '当前用户',
}
comments.push(newComment)
return NextResponse.json(newComment, {status: 201})
}
/components/ComponentList.tsx
"use client"
import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card";
interface Comment {
id: number;
text: string;
author: string;
}
export function CommentList({comments}: { comments: Comment[] }) {
return (
<Card className="mb-6">
<CardHeader>
<CardTitle>评论列表</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-4">
{comments.map((comment) => (
<li key={comment.id} className="border-b pb-4">
<p className="font-medium">{comment.author}</p>
<p className="text-gray-600">{comment.text}</p>
</li>
))}
</ul>
</CardContent>
</Card>
)
}
/components/ComponentDialog.tsx
"use client"
import React, {useState} from 'react';
import {Button} from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useRouter} from 'next/navigation';
import {toast} from "sonner"
export function CommentDialog() {
const [open, setOpen] = useState(false);
const [comment, setComment] = useState('');
const router = useRouter();
const handleSubmit = async (e: React.FormEvent) =>
{
e.preventDefault();
try {
const response = await fetch("/api/comments", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({text: comment}),
})
if (!response.ok) {
throw new Error("提交失败")
}
toast("评论提交成功")
setOpen(false);
router.refresh()
setComment('')
} catch (error) {
toast("提交评论时出错")
}
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline">添加评论</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>添加新评论</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="comment">评论内容</Label>
<Input
id="comment"
value={comment}
onChange={(e) => setComment(e.target.value)}
required
/>
</div>
<Button type="submit">提交</Button>
</form>
</DialogContent>
</Dialog>
)
}
/app/layout.tsx
import "./globals.css";
import { Toaster } from "@/components/ui/sonner"
import React from "react";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className="antialiased"
>
<main>
{children}
</main>
<Toaster />
</body>
</html>
);
}
/app/page.tsx
import {CommentList} from "@/components/CommentList";
import {CommentDialog} from "@/components/CommentDialog";
export default async function Home() {
const response = await fetch("http://localhost:3000/api/comments", {
method: "GET",
headers: {"Content-Type": "application/json"}
});
const comments = await response.json();
return (
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10">
<div className="w-full max-w-sm">
<h1 className="text-2xl font-bold mb-6">评论系统</h1>
{/* 服务端获取的评论数据传递给客户端组件 */}
<CommentList comments={comments} />
{/* 添加评论的对话框 */}
<CommentDialog />
</div>
</div>
);
}