Careeroza
Preloading and Deduplication
The Same Data in Many Components
The layout, the page and generateMetadata often need the same product. Instead of passing it down through props, each one calls getProduct(id). Duplicate fetch GET calls in one render are memoised automatically; for ORM calls wrap the function in React's cache().
import 'server-only';
import { cache } from 'react';
import { prisma } from '@/lib/prisma';
export const getProduct = cache(async (id: number) => {
return prisma.product.findUnique({ where: { id } });
});How cache() Works
Example
A per-request memo: the first call starts the work; later calls with the same argument get the same Promise.
function cache<A extends string | number, R>(fn: (arg: A) => Promise<R>) {
const memo = new Map<A, Promise<R>>();
return (arg: A): Promise<R> => {
if (!memo.has(arg)) memo.set(arg, fn(arg));
return memo.get(arg)!;
};
}
let dbQueries = 0;
const getProduct = cache(async (id: number) => {
dbQueries++;
return { id, name: 'Product ' + id };
});
async function main() {
// generateMetadata, layout and page all ask for product 42
const [a, b, c] = await Promise.all([getProduct(42), getProduct(42), getProduct(42)]);
await getProduct(7);
console.log(a.name, b === c ? '(same object)' : '(different)');
console.log('DB queries:', dbQueries);
}
main();Output
Product 42 (same object) DB queries: 2
Preload Pattern
Start a request early, before a check that must run first, and use the result later:
export const preload = (id: number) => {
void getProduct(id); // start now, do not await
};
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const id = Number((await params).id);
preload(id); // begins loading the product
const allowed = await checkRegionAvailability(); // runs at the same time
if (!allowed) return <p>Not available in your region</p>;
const product = await getProduct(id); // usually already done
return <h1>{product?.name}</h1>;
}