Double lookup despite using cache
Unanswered
Wuchang bream posted this in #help-forum
Wuchang breamOP
In this code resolvePlayer is being called twice, when player was not found in the resolvePlayer method, is there a way to fix that?
import { Metadata } from 'next';
import { redirect } from 'next/navigation';
import { cache } from 'react';
import redis from '~/lib/redis';
import { sanitizeString } from '~/lib/security';
import { fetchAllPlayerStatsAction } from '~/app/private-server-actions/rankingActions';
type PageProps = {
params: Promise<{ player: string }>;
};
// Function to resolve player name and UUID with caching
const resolvePlayer = cache(async (player: string): Promise<PlayerData> => {
//code logic
return { /user/ };
});
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { player } = await params;
const playerData = await resolvePlayer(player);
return {
title: `${playerData.name}`,
description: ``,
keywords: [''],
};
}
export default async function GraczPage({ params }: PageProps) {
const { player } = await params;
const playerData = await resolvePlayer(player);
// If player doesn't exist, redirect to ranking page with notification
if (!playerData.exists) {
redirect('/');
}
// Pre-fetch all player stats server-side with ISR caching
const playerStats = await fetchAllPlayerStatsAction(playerData.uuid);
return (
<>
//view
</>
);
}8 Replies
Western paper wasp
resolvePlayer is called both in generateMetadata and in the page component itself. These are two different render invocations, and cache() doesn’t share the result between them.You can move data fetching to a single place, for example only in the page and pass it into metadata, or accept this behavior as normal. That’s how App Router and Metadata work.
For full control, use an external cache such as Redis or a database instead of
react.cache@Western paper wasp `resolvePlayer` is called both in `generateMetadata` and in the page component itself. These are two different render invocations, and `cache()` doesn’t share the result between them.
You can move data fetching to a single place, for example only in the page and pass it into metadata, or accept this behavior as normal. That’s how App Router and Metadata work.
For full control, use an external cache such as Redis or a database instead of `react.cache`
Greater Shearwater
But isn't OP using the same pattern shown in the docs?
https://nextjs.org/docs/app/getting-started/metadata-and-og-images#memoizing-data-requests
One call is inside the
Or am I missing something?
https://nextjs.org/docs/app/getting-started/metadata-and-og-images#memoizing-data-requests
One call is inside the
metaData function and one call is in the page component function. Same as in docs.Or am I missing something?
Western paper wasp
The pattern from the docs looks similar, but
If you need a single real request, move the data fetching into the page and pass it into metadata, or use an external cache such as Redis or a database instead of
react.cache() isn’t shared between generateMetadata and page. Those are different render contexts. The docs show a memoized fetch, but it will still run twice, once for metadata and once for the page. This is expected behavior in the App Router.If you need a single real request, move the data fetching into the page and pass it into metadata, or use an external cache such as Redis or a database instead of
react.cache.@Western paper wasp The pattern from the docs looks similar, but `react.cache()` isn’t shared between `generateMetadata` and `page`. Those are different render contexts. The docs show a memoized fetch, but it will still run twice, once for metadata and once for the page. This is expected behavior in the App Router.
If you need a single real request, move the data fetching into the page and pass it into metadata, or use an external cache such as Redis or a database instead of `react.cache`.
Greater Shearwater
I don't think that's correct.
The whole purpose of
For example, I tested with the below setup now, and the console log only gets printed one time. Not twice. Even though the function is called in
And if I remove the
Happy to be corrected if I am missing something.
The whole purpose of
cache() from React is to cache (memoize) the function, so that the function body doesn't run multiple times for the same render. And both metadata and the page functions run in the same render, so the fetch will only run one time, thanks to the cache().For example, I tested with the below setup now, and the console log only gets printed one time. Not twice. Even though the function is called in
metaData and as well as in the page.import { Metadata } from "next"
import { cache } from "react"
const getSomething = cache(async () => {
await new Promise((resolve) => setTimeout(resolve, 2000))
console.log("Function executed.")
})
export async function generateMetadata(): Promise<Metadata> {
await getSomething()
return {
description: "Description",
title: "Title",
}
}
export default async function TestPage() {
await getSomething()
return <h1>Hello, World!</h1>
}And if I remove the
cache, then the console log gets printed twice.Happy to be corrected if I am missing something.
Western paper wasp
You’re right that
react.cache() does memoize a call within a single render pass, and in a simple generateMetadata + page example it can look like a single call. My previous reply was about real world behavior in the App Router, where generateMetadata and page are not guaranteed to share the exact same render context in all modes (RSC, streaming, prefetch, ISR, edge). In those cases, the react.cache() result may not be reused, and the request can be executed again. So if you need strictly one real network request, the safe option is to move the fetch into page and pass the data into metadata, or use an external cache (Redis/DB) instead of relying on react.cache().Western paper wasp
react.cache() caches only within a single render,generateMetadata() and page can be rendered separately (different contexts, streaming, ISR, prefetch). That’s why the function may be called twice, and this is normal in the App Router.How to guarantee a single real request:
- Perform the request only in
page- Reuse the result for both the page and metadata (via arguments/object)
- Or use an external cache (Redis/DB)
react.cache() is not a guarantee of a single request. For 100% certainty, use a single fetch or an external cache