Set Metadata Dynamically Without Duplicate Requests - NextJS 13
Asked Answered
M

2

19

Consider the following server side component that's part of some page (page.js) in the app directory, representing a person:

export default async function Person({ params }) {
    const person = await getPerson(params.id);
    
    return (
        <h1> {person.name} </h1>
        <p> {person.bio} </p>
    )
}

Obviously, the intent here is to dynamically render new pages, with the idea being that the URL looks something like /person/{id}, where ID is the unique ID of each person.

The issue I'm running into is that I need to set metadata for the page. For example, I want the title to be person.name, and the description to be person.description. From what I'm reading, the only way to do that in NextJS 13 with the app directory is to use generateMetadata, which would look something like this:

export async function generateMetadata({ params }) {
    const person = await getPerson(params.id);

    return {
        title: person.name,
        description: person.description
    }
}

I'm sure you can see the issue now: I need to make the exact same request (getPerson) twice in the same page. From what I've seen, there isn't a way to pass data between generateMetadata and the page component.

The <Head> component from next/head seems like the ideal solution, but it doesn't appear to work in the app directory with NextJS 13.

Am I missing something really obvious here? Or is this an absolutely massive oversight by Next's devs?

Melmela answered 24/6, 2023 at 1:53 Comment(0)
G
16

If the function you're calling is internally calling fetch it is automatically deduplicated by Next.js as can be read in the official documentation here.


Should you be directly calling a database or using a fetch alternative (like axios) as explained you can use Reacts new cache() function to deduplicate calls to logic that does not automatically get deduped by Next.js. More can be found in the official documentation here

import { cache } from 'react'
 
export const getPerson = cache(async () => {
   // your logic ...
});

Although the getPerson() function is called twice, inside the page and metadata generator, only one call will be made. This is because the function is wrapped in cache(), so the second request can reuse the result from the first request.

Grapher answered 27/6, 2023 at 13:54 Comment(5)
interesting, thanks so much. I should’ve specified, but I am using fetch underneath getPersonMelmela
looks like I spoke too soon - when testing with my backend, Next is in fact making duplicate requests when using fetch underneath getPerson (which turns into a single request when generateMetadata is removed). Haven't tested it in production, so it may just be an issue in the development server. Going to give cache a try...Melmela
@TwistedTea Things work fine in production github.com/vercel/next.js/issues/53035#issuecomment-1646559023Emylee
@RishabSharma Ha, good find, thanks. Next13's dev environment is a buggy disasterMelmela
@TwistedTea I suspect this is by design, otherwise using hot reload you might encounter stale data etc. The same way static routes are dynamic on development.Grapher
M
0

In NextJS data requests are cached unless you opt out according to official doc on the data cache

React extends the fetch API to automatically memoize requests that have the same URL and options. This means you can call a fetch function for the same data in multiple places in a React component tree while only executing it once.

Miosis answered 20/9, 2023 at 15:59 Comment(4)
OP is not using fetch, asking for a server side call.Lehrer
This is about server side call.Miosis
Oh, I meant server action. Still not a valid reply though. Only fetch api's will get memoized automatically, OP is not using fetch.Lehrer
@CanUysal I am using fetch underneath that async call; I probably should've clarified that.Melmela

© 2022 - 2024 — McMap. All rights reserved.