在NextJS 13中使用TanStack Query实现图片数据即时更新
解决NextJS 13 + TanStack Query图片数据即时更新问题
一、图片删除功能修复
1. 同步更新分类查询数据
当前删除操作仅更新了全局图片列表的查询["images"],但用户若处于分类视图(selectedCategory !== "all"),分类对应的查询["images", selectedCategory]未同步更新,导致界面无变化。需在onMutate中同时处理分类数据:
onMutate: async (url: string | null | undefined) => { // 取消全局和当前分类的查询请求 await queryClient.cancelQueries({ queryKey: ["images"] }); if (selectedCategory && selectedCategory !== "all") { await queryClient.cancelQueries({ queryKey: ["images", selectedCategory] }); } // 保存原始数据用于回滚 const previousImages = queryClient.getQueryData(["images"]); const previousCategoryImages = selectedCategory && selectedCategory !== "all" ? queryClient.getQueryData(["images", selectedCategory]) : null; // 更新全局图片列表 queryClient.setQueryData( ["images"], (old: string[] | undefined) => old?.filter((image) => image !== url) ?? [] ); // 更新当前分类的图片列表 if (selectedCategory && selectedCategory !== "all") { queryClient.setQueryData( ["images", selectedCategory], (old: string[] | undefined) => old?.filter((image) => image !== url) ?? [] ); } return { previousImages, previousCategoryImages }; },
2. 错误回滚时恢复分类数据
在onError中同时回滚全局和分类的原始数据:
onError: (error, _, context) => { console.error("Error deleting the image:", error); toast.error(`${error}`, { toastId }); // 回滚全局数据 if (context?.previousImages) { queryClient.setQueryData(["images"], context.previousImages); } // 回滚分类数据 if (context?.previousCategoryImages && selectedCategory && selectedCategory !== "all") { queryClient.setQueryData(["images", selectedCategory], context.previousCategoryImages); } },
二、图片上传功能修复
1. 修正数据类型不匹配问题
当前上传的onMutate直接将FormData加入URL数组,与后端返回的string[]格式冲突,导致乐观更新无效。需先从后端获取真实URL:
首先修改mutationFn,返回后端返回的新图片URL:
mutationFn: async (formData: FormData) => { const response = await fetch(`${process.env.BACKEND_URL}/upload`, { method: "POST", body: formData, headers: { authorization: `Bearer ${session?.user.accessToken}`, }, }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || "Upload failed"); } // 假设后端返回的data是图片URL字符串,根据实际格式调整 return data; },
2. 正确更新查询数据
在onSuccess中用后端返回的真实URL更新全局和分类查询:
onSuccess: async (newImageUrl) => { // 更新全局图片列表 queryClient.setQueryData(["images"], (old: string[] | undefined) => old ? [...old, newImageUrl] : [newImageUrl] ); // 更新对应分类的图片列表 if (selectedCategory) { queryClient.setQueryData(["images", selectedCategory], (old: string[] | undefined) => old ? [...old, newImageUrl] : [newImageUrl] ); } // 或直接失效查询,重新获取最新数据 await queryClient.invalidateQueries({ queryKey: ["images"] }); if (selectedCategory) { await queryClient.invalidateQueries({ queryKey: ["images", selectedCategory] }); } toast.success("Image uploaded successfully", { toastId }); },
3. 修正提示逻辑
将成功/错误提示移到对应钩子,避免错误场景显示成功提示:
onError: (error) => { console.error("Upload error:", error); toast.error(`${error.message}`, { toastId }); }, onSettled: () => { setSelectedFile(null); setImageTitle(""); setSelectedCategory(""); },
三、其他优化
1. 移除无用的useEffect
ImagesPage中以下useEffect是多余的,useQuery会自动根据selectedCategory变化重新请求,直接删除:
// 移除该代码块 useEffect(() => { if (selectedCategory) { handleImagesByCategory({ category: selectedCategory, session }); } }, [selectedCategory]);
2. 处理undefined数据
在setQueryData时处理old为undefined的情况,避免报错:
// 示例:删除时的全局数据更新 queryClient.setQueryData( ["images"], (old: string[] | undefined) => old ? old.filter((image) => image !== url) : [] );
内容的提问来源于stack exchange,提问作者Oleh
相关产品推荐
相关产品推荐

