Skip to content
Chapter 32Lesson 8

Quiz - The Cache Components rendering model

Quiz progress

0 / 0

You’ve finished an org-scoped invoices dashboard. It reads cookies() for the session and queries the database per request, and it has no 'use cache' anywhere. A teammate flags it in review: “this whole page is dynamic, that’s a performance smell — cache it.” Under Cache Components, what’s the right response?

The dashboard is fully dynamic and that’s the correct shape — it shows different data per user, so there’s nothing user-agnostic to prerender. You cache the chrome around such pages, not the per-user data inside them.
Agreed — wrap the page in 'use cache' so it prerenders, then read the cookies outside the cached scope to keep it correct.
Agreed — a route with no caching falls back to the legacy implicit model, so add at least one 'use cache' boundary to opt into the Next.js 16 model.

A dashboard route is already dynamic (it renders per request). One widget deep inside it runs a slow-ish query. A reviewer suggests wrapping just that widget in 'use cache' to “speed up the page.” What’s the trade-off they’re missing?

The page is already paying for a request-time render, so caching one child doesn’t change its transport mode — you gain a small, conditional speedup but take on a permanent freshness liability where the widget can go stale against the live data around it.
Caching one child of a dynamic page promotes the whole route into a static shell, so the rest of the page silently stops rendering per request.
A 'use cache' widget inside a dynamic parent is a build error, so the suggestion can’t ship at all.

A cached fetcher reaches a value from its enclosing scope rather than taking it as an argument:

export async function Dashboard({ userId }: { userId: string }) {
async function getData(filter: string) {
'use cache';
return db.query.events.findMany({
where: and(eq(events.userId, userId), eq(events.kind, filter)),
});
}
return <Events rows={await getData('errors')} />;
}

Which statements about this code are correct? Select all that apply.

userId is captured from the closure and folded into the cache key alongside filter, so two users with the same filter get two distinct entries.
'use cache' forbids capturing outer variables, so this fails to compile until userId is passed in as an argument.
If getData instead captured Date.now() from the outer scope, that timestamp would be frozen into the entry and reused on later reads rather than re-evaluated per request.

You’re deciding the lifetime for a cached product catalog that admins edit a few times a day through your own admin panel. The reflex “cache it as long as possible, cacheLife('max'), done” is half-right. What’s the senior shape, and why?

cacheLife('max') paired with a tagmax stops the pointless clock-based refresh, and the tag lets the admin’s save push an invalidation the instant the data changes. max with no tag is a footgun: nothing refreshes it on demand.
cacheLife('max') alone is correct — max means the entry never goes stale, so an explicit invalidation is redundant.
cacheLife('seconds') — a short timer is the only way to reflect admin edits quickly, since tags can’t invalidate a max-lifetime entry.
Don’t cache it — any data that changes can’t be safely cached, so the catalog must render dynamically.

You have two functions in your data layer: getCurrentUser() (reads the session via headers(), called from the layout, the page, and a deep nav in one render) and getProductCatalog() (a request-independent fetch every user should share). Which caching primitive goes on each?

cache() (from react) on getCurrentUser and 'use cache' on getProductCatalog — the hinge is whether the work reads request data.
'use cache' on both — it deduplicates within a render anyway, so it covers the per-render case too.
cache() on both — a single primitive for all deduplication keeps the data layer consistent.

A user edits invoice #42 in a form and your Server Action redirects them straight to the invoice’s detail page. The list and detail are cached. To guarantee they see their own edit on that next screen, which invalidation tool belongs at the revalidate seam?

updateTag — it expires the tagged entries so the redirect’s render blocks for fresh data, delivering read-your-writes.
revalidateTag(tag, 'max') — stale-while-revalidate is the safe default for every mutation, including form submits.
router.refresh() after the redirect — it re-pulls the route’s Server Components, which clears the stale cache entry.

You inherit a route that starts with export const revalidate = 3600, and the Next.js 16 build rejects it. What’s the correct migration?

Move the freshness to the data read: wrap the fetcher in 'use cache' and set cacheLife('hours') — the preset closest to the interval, matched to the data’s shape rather than a hand-rolled second count.
Replace it with await connection() at the top of the page so the route opts back into timed revalidation.
Replace it with cacheLife({ revalidate: 3600 }) at module scope, keeping the exact one-hour interval.

Quiz complete

Score by topic