我正在研究的示例非常简单。我有一个非常简单的 NextJS 应用程序(使用应用程序目录),它连接到 Supabase 数据库。这是我第一次尝试使用app目录——我以前专业使用过NextJS,但只使用过页面路由器。
为了了解一些背景信息,我的应用程序允许用户创建活动并将客人添加到这些活动中。所有更新均以非常标准的方式在客户端组件上完成:
读取全部在异步服务器组件中完成:
问题在这里:尽管我可以看到数据库具有所有正确的行,但我得到的似乎是使用 SELECT API 在服务器组件中返回的陈旧版本。这会导致 UI 显示陈旧数据,并使我的应用程序几乎无法使用。我尝试记录 API 返回的结果,并且日志还显示过时的数据 - 与进入实际 UI 和客户端组件的数据相同。
有趣的是,我注意到,如果我更新一些代码并触发模块刷新,我就会开始获取最新数据。我深入研究了 NextJS 缓存,发现存在大量与缓存相关的常见混乱,其他人也看到了这个问题。然而,我已经尝试了他们所有的解决方法,但我无法避免这种缓存。目前,我不确定这只是 Next,还是 Supabase 也在做一些让缓存清除变得特别具有挑战性的事情。
这是我到目前为止所尝试的,正如 NextJS 文档和 GitHub 问题中所建议的那样:
export const revalidate = 0
和
export const dynamic = 'force-dynamic'
unstable_noStore()
到不同的根组件,包括获取数据的核心服务器组件
createClient
调用以创建全局
fetch
覆盖,这基本上只是标准浏览器获取,没有额外的缓存魔法
如果有一个明显的解决方案,有人可以指出我缺少什么吗?
一旦数据被缓存,它将一直保持这种状态,直到您显式触发缓存的重新验证。
让我将其分为两个简单的步骤:
这是我在自己的项目中使用的一个示例:
import type { Database } from "@/types/database";
import { createBrowserClient } from "@supabase/ssr";
import { unstable_noStore } from "next/cache";
const fetchWithTags = (
input: RequestInfo | URL,
init?: RequestInit,
tag?: string,
): Promise<Response> => {
return fetch(input, {
...init,
next: {
...(init?.next || {}),
tags: tag ? [tag] : [],
},
});
};
interface Props {
tag?: string;
noStore?: boolean;
}
export const createClient = (
{ tag, noStore }: Props = { tag: undefined, noStore: false },
) => {
if (noStore) {
unstable_noStore();
}
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
global: {
fetch: (input: RequestInfo | URL, init?: RequestInit) =>
fetchWithTags(input, init, tag),
},
},
);
};
创建 Supabase 客户端时,您可以像这样指定标签:
createClient({ tag: "main/hero" });
然后,在修改数据后,您可以在路由处理程序中或任何适当的地方重新验证标记:
...
revalidateTag("main/hero");
...
这将确保 Next.js 在发生更改时获取最新数据。或者,如果您真的不关心设置标签,只需使用
noStore
选项,这样所有内容都不会被缓存。