`React.cache` doesn't work when using PPR
Answered
Asian paper wasp posted this in #help-forum
Asian paper waspOP
So I have specified a query as follow:
And then I use it in
In which, the
The idea is to:
1. DB is queried only once in the blog listing page
2. The result of
// queries.ts
'use server';
import { cache as reactCache } from 'react';
export const getBlogsMetadataByIds = reactCache(async (ids: string[]) => {
noStore();
const blogsMetadata = await prisma.blogMetadata.findMany({
where: { id: { in: ids } },
include: { _count: { select: { likes: true } } },
});
return blogsMetadata.map(({ _count, ...rest }) => ({
...rest,
like: _count.likes,
}));
});And then I use it in
Views// views.tsx
export const Views: FC<ViewsProps> = async ({
blogIds,
blogId,
...props
}) => {
const metadata =(await getBlogsMetadataByIds(blogIds)).find(({ id }) => id === blogId)
return (
<Typography aria-label="Blog views" startDecorator={<Eye />} {...props}>
{numberFormatter.format(metadata?.view ?? 0)}
</Typography>
);
};In which, the
Views component is being used in the blog listing page.// /app/blog/page.tsx
const Blogs: FC = async () => {
// yes, blogs and blogMetadata are stored in two different places. Hence 2 seperated API calls.
const blogs = await getBlogs({ page: 1 });
return (
<div>
{blogs.map(({ id }) => (
<Card>
// using PPR here
<Suspense fallback={<ViewsSkeleton />}>
<Views
blogId={id}
blogIds={blogs.map(({ id }) => id)}
/>
</Suspense>
</Card>
))}
</div>
)
}The idea is to:
1. DB is queried only once in the blog listing page
2. The result of
getBlogsMetadataByIds is NOT cached in other server requests. i.e. when the user refreshes the page or other users visit the same page, it should see the up to date metadataAnswered by Asian paper wasp
So instead of
which creates a new
const Blogs: FC = async () => {
// yes, blogs and blogMetadata are stored in two different places. Hence 2 seperated API calls.
const blogs = await getBlogs({ page: 1 });
return (
<div>
{blogs.map(({ id }) => (
<Card>
// using PPR here
<Suspense fallback={<ViewsSkeleton />}>
<Views
blogId={id}
blogIds={blogs.map(({ id }) => id)}
/>
</Suspense>
</Card>
))}
</div>
)
}which creates a new
blogIds everytime, I should doconst Blogs: FC = async () => {
// yes, blogs and blogMetadata are stored in two different places. Hence 2 seperated API calls.
const blogs = await getBlogs({ page: 1 });
const blogIds = blogs.map(({ id }) => id)
return (
<div>
{blogs.map(({ id }) => (
<Card>
// using PPR here
<Suspense fallback={<ViewsSkeleton />}>
<Views
blogId={id}
blogIds={blogIds}
/>
</Suspense>
</Card>
))}
</div>
)
}4 Replies
Asian paper waspOP
This used to, and in theory should work, because React will invalidate the cache for all memoized functions for each server request. See https://react.dev/reference/react/cache#caveats
Yet, for whatever reasons, currently the DB is being queried every time the function is called as if
Yet, for whatever reasons, currently the DB is being queried every time the function is called as if
React.cache is not used at all.Tested by adding a
console.log in getBlogsMetadataByIds. The message shouldn't log multiple times if React.cache is functioning.Asian paper waspOP
OK... the issue is
Asian paper waspOP
So instead of
which creates a new
const Blogs: FC = async () => {
// yes, blogs and blogMetadata are stored in two different places. Hence 2 seperated API calls.
const blogs = await getBlogs({ page: 1 });
return (
<div>
{blogs.map(({ id }) => (
<Card>
// using PPR here
<Suspense fallback={<ViewsSkeleton />}>
<Views
blogId={id}
blogIds={blogs.map(({ id }) => id)}
/>
</Suspense>
</Card>
))}
</div>
)
}which creates a new
blogIds everytime, I should doconst Blogs: FC = async () => {
// yes, blogs and blogMetadata are stored in two different places. Hence 2 seperated API calls.
const blogs = await getBlogs({ page: 1 });
const blogIds = blogs.map(({ id }) => id)
return (
<div>
{blogs.map(({ id }) => (
<Card>
// using PPR here
<Suspense fallback={<ViewsSkeleton />}>
<Views
blogId={id}
blogIds={blogIds}
/>
</Suspense>
</Card>
))}
</div>
)
}Answer