NextJS SSR实现无页面重载删除图片后刷新数据方案
解决NextJS SSR删除图片后客户端即时移除的问题
问题核心在于你的Dashboard组件是SSR渲染,文件列表files是服务器端一次性获取的静态数据,客户端没有维护状态,所以删除操作后无法触发页面重新渲染。以下是具体修改方案:
步骤1:将Dashboard改为客户端组件并维护文件状态
在Dashboard中添加"use client"指令,用React的useState管理文件列表,让客户端可以动态更新数据:
"use client" // 转为客户端组件 import { getAllFilesFromFolder } from '../utils/fileUtils'; import ImageCard from "../components/imagecard"; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import { useState } from 'react'; // 引入状态管理 const path: any = process.env.FOLDER_PATH; const initialFiles = getAllFilesFromFolder(path); export default function Dashboard() { // 用useState维护文件列表状态 const [files, setFiles] = useState(initialFiles); // 定义删除回调,接收要删除的图片src const handleImageDelete = (deletedSrc: string) => { const deletedFileName = deletedSrc.split('/').pop(); setFiles(prevFiles => prevFiles.filter(file => { const fileName = file.split('\\').pop() || file; return fileName !== deletedFileName; })); }; return ( <main> <ToastContainer position="top-right" autoClose={5000} hideProgressBar={false} newestOnTop={false} closeOnClick rtl={false} pauseOnFocusLoss draggable pauseOnHover theme="dark" /> <div className="mt-12 mx-20"> <div className="grid grid-cols-4 gap-5"> {files.map((file) => { const fileName = file.split('\\').pop() || file; const src = `/api/images/${fileName}`; // 改用fileName作为key,避免index导致的渲染异常 return <ImageCard key={fileName} src={src} onDelete={handleImageDelete} />; })} </div> </div> </main > ); }
步骤2:修改ImageCard组件,接收删除回调并触发更新
更新ImageCard的Props,添加onDelete回调,在删除请求成功后调用它,通知父组件移除对应图片:
"use client" import { ClipboardIcon, TrashIcon } from "@radix-ui/react-icons"; import { Button, Card, Flex, Inset, Tooltip } from "@radix-ui/themes"; import Image from "next/image"; import { useState } from "react"; import { toast } from "react-toastify"; import DeleteButtonDialog from "./deletebutton"; // 更新Props,添加删除回调 interface ImageCardProps { src: string; onDelete: (src: string) => void; } const ImageCard: React.FC<ImageCardProps> = ({ src, onDelete }) => { const [copied, setCopied] = useState(false); const copyLinkToClipboard = () => { navigator.clipboard.writeText(window.location.host + src) .then(() => { toast.success('Link erfolgreich kopiert!', { position: "top-right", autoClose: 5000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: true, progress: undefined, theme: "dark", }); setCopied(true); setTimeout(() => setCopied(false), 2000); }) .catch((error) => { console.error('Failed to copy:', error); }); }; const handleDelete = (confirmed: boolean) => { if (confirmed) { fetch(src, { method: "DELETE" }) .then((response) => { if (response.ok) { toast.success('Bild erfolgreich gelöscht!', { position: "top-right", autoClose: 5000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: true, progress: undefined, theme: "dark", }); // 删除成功后调用父组件的回调,传递当前图片src onDelete(src); } else { console.error("Failed to delete image"); } }) .catch((error) => { console.error("Error while deleting image:", error); }); } }; return ( <main> <Card size="2" style={{ maxWidth: 600 }}> <Inset clip="padding-box" side="top" pb="current"> <Image src={src} width={1000} height={760} className="hidden md:block" alt="Image Card" /> </Inset> <Flex gap="3"> <Tooltip content="Link kopieren"> <Button onClick={copyLinkToClipboard} variant="soft"> <ClipboardIcon width="16" height="16" /> </Button> </Tooltip> <DeleteButtonDialog onDelete={handleDelete}></DeleteButtonDialog> </Flex> </Card> </main > ); } export default ImageCard;
关键说明
- 将Dashboard转为客户端组件并使用
useState维护文件列表,让客户端能动态更新数据并触发重新渲染。 - 通过
onDelete回调实现父子组件通信,删除成功后立即通知父组件移除对应条目,无需刷新页面。 - 把列表项的key从
index改为fileName,确保每个列表项的唯一性,避免React渲染异常。
内容的提问来源于stack exchange,提问作者TryAnixx
相关产品推荐
相关产品推荐

