Next.js Discord

Discord Forum

Passing down server data

Answered
nehalist posted this in #help-forum
Open in Discord
Avatar
I do have a layout that requires to query some data via the url; think of something like /products/foo - and I want the layout to show information of the foo product on all product pages.

Now I do have routes like /products/foo/manage, where you can edit the product.

Currently my layout (at /products/layout.tsx) queries the product - the same way my /products/manage/page.tsx does.

That's not really performance friendly - but I have honestly no idea how to improve this. Following https://nextjs.org/docs/app/building-your-application/data-fetching/patterns I should use fetch for things like that - but in fact my data is queried without fetch:

export default async function ProductManagePage({
  params: { slug },
}: {
  params: { slug: string };
}) {
  const product = await getProductBySlug(slug);
  if (!product) {
    return notFound();
  }
  // ...


The getProductBySlug is a database query (via Prisma).

I guess I'm supposed to use React cache - but I don't understand the invalidation approach here. If this is invalidated for each server request, there'd be still multiple db queries for the manage page?
Answered by necm1
a different approach could be to write a hook useContext, wrap it in your layout and make it accessible to all underlaying pages / components
View full answer

67 Replies

Avatar
so your issue is basically that the query would re-run 2 times?
in first place; It isn't really ideal to fetch data in layout files, especially in your case if you are forced to fetch the data again in your ProductManagePage component, to pass them down to child components
Avatar
a different approach could be to write a hook useContext, wrap it in your layout and make it accessible to all underlaying pages / components
Answer
Avatar
but the context wouldn't be available within server components?
Avatar
you cant use the context in your server component, but you can initialize the wrapping component in your layout.tsx which will pass the data to the hook itself
which makes the data accessible in the context itself in client components
// Your Layout

  return <html>
    <body>
      // Some components
        <ProductProvider product={// fetched data here}>
           {children}
        </ProductProvider>
    </body>
  </html>


// any client component

const productContext = useContext(ProductContext);

console.log(productContext)
but as mentioned before; it's not a good approach to fetch data in layout.tsx
Avatar
guess I can't get my head around that yet;

if my layout provides some context that can only be used within client components, I still can't use the product data on my pages, except my pages simply integrate client components?
I see no other way to do that. my product page does include a header, that always shows information regarding the product and some sub pages. manage is just one of them; there's also things like reviews, which would also require some product information.
Avatar
the relevant question here is; do you need the data in your server component?
if you want to do some CRUD actions on top of the server, you could simply use server actions, which runs the query on server level
so I guess the header itself is a client component?
// Your Layout

  return <html>
    <body>
      // Some components
        <Header product={} />
        <ProductProvider product={// fetched data here}>
           {children}
        </ProductProvider>
    </body>
  </html>
would lead to the same way
Avatar
but that's still fetching data in the layout? ๐Ÿ˜„
Avatar
I mean, it isn't suggested to fetch data in layout
a good practise
but in this case
Avatar
just refering to your "it's not a good approach to fetch data in layout.tsx" - I wouldn't know a different way of achieving the same thing, except rendering the header on every page - which would kinda eliminate the purpose of layouts
Avatar
only thing why it isn't mostly suggested is because you could need the data in your page, where you basically pass it down to the client components inside the page
ye
because of that; you could simply fetch it one time, pass it down to necesseary client components and providing a Provider, which passes the data down to a context accessible in client components
so you don't have to fetch again in your page sever component
does it make sense?
Avatar
still, using my page.tsx files only to render client components seems very odd.
yeah I get, it just feels weird ๐Ÿ˜„
Avatar
may I ask why you would need the data inside your page component
so what would be the purpose of it
Avatar
so my page.tsx become something like "dumb components" as it was refered to some time ago ๐Ÿ˜„
Avatar
if you aim to do something with the data like updating view count or smth else, you have still the slug and could directly run a query, but without the needed data from layout
another way could be storing the feteched data in cookies, which is accessible on server level
Avatar
it's probably not that I need the data within my page. it just feels a bit odd to just use my pages for fetching and rendering client components.

so my manage page would become a page.tsx that would just render a ManagePage client component - even the naming seems odd, because my page is already ManagePage ๐Ÿ˜„
Avatar
I mean... its kinda a bad architecture from Next.js
Avatar
that sounds like a horrendous idea, since this is an entire product object, with possibly hundreds of props ๐Ÿ˜ฎ
Avatar
but I guess thats a simple way to avoid fetching data multiple times, without using external packages
another way could be using Jotai
Avatar
hmm, I sometimes miss trpc... that made things a lot easier :/
Avatar
which makes data accessible in server components as a "state"
but the integration of Jotai and whole setup is more a pain in the a**
Avatar
well, I'll try out the context approach, but for now 2 or more requests during dev is fine. it's just something I need to fix at some point
Avatar
import { ManagerPage as Page } from '.....' often solves issues like this - saw this appraoch even in larger OS projects
Avatar
I get why it works - it just feels terrible from a dx perspective
don't give me a page.tsx then, give me a data.tsx that fetches data, and all components are client components
Avatar
another way could be using Redis, so fetching the data from RAM is more effective than the harddrive
its also good practise to always use server components and last choice would be using client components
but I guess in this case; it's the best option and easiest way to handle this
Avatar
occasionally there's no way around client components, sadly
I try to do as much as possible on the server
Avatar
guess thats why they gave us server actions ๐Ÿ˜ญ
found a good example for this case
Image
Avatar
sometimes it feels like this was a step backwards. I remember when trying out trpc with the pages router everything worked so smoothly and I was blown away by the dx. now you need canary react version in order to know if a form is submitting or not... O_o
Avatar
feel ya
btw. if my suggestion was helpful, would be awesome if you could mark my answer as described here ๐Ÿ˜ญ
โค๏ธ
Avatar
unfortunately this doesn't work, since I can't create a context within server components
Avatar
context is reserved for client components only
bust you still pass the data from the provider into the context itself
then use useContext() inside the client component to receive the data
Avatar
Black-throated Blue Warbler
You dont really have to prop dril, you can fetch data on your page, then fetch it again in a server component within your page and you are only going to see one request. It should be automatically de-duped
Avatar
how does "automatically" work here?
Avatar
and how to invalidate that cache?
Avatar
Black-throated Blue Warbler
documentation for that should be on the same page if you scroll down a bit ๐Ÿ˜‰
you can assign a cache tag to any request and use revalidateTag('yourCacheTag') or revalidate all requests for an entire path with revalidatePath('your-path')
Avatar
since I'm not using fetch, the documentation on that is just one small section - which doesn't tell anything about invalidation. if I head over to the react cache docs it states

React will invalidate the cache for all memoized functions for each server request.

which renders it useless, because I make multiple multiple requests by making one in the layout and one in the page.
Avatar
are you using external libraries like axios?