Next.js Discord

Discord Forum

Passing down server data

Answered
nehalist posted this in #help-forum
Open in Discord
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

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
but the context wouldn't be available within server components?
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
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?
@necm1 but as mentioned before; it's not a good approach to fetch data in layout.tsx
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.
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
// Your Layout

  return <html>
    <body>
      // Some components
        <Header product={} />
        <ProductProvider product={// fetched data here}>
           {children}
        </ProductProvider>
    </body>
  </html>
would lead to the same way
but that's still fetching data in the layout? ๐Ÿ˜„
I mean, it isn't suggested to fetch data in layout
a good practise
but in this case
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
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
@necm1 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
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?
still, using my page.tsx files only to render client components seems very odd.
yeah I get, it just feels weird ๐Ÿ˜„
may I ask why you would need the data inside your page component
so what would be the purpose of it
so my page.tsx become something like "dumb components" as it was refered to some time ago ๐Ÿ˜„
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
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 ๐Ÿ˜„
I mean... its kinda a bad architecture from Next.js
@necm1 another way could be storing the feteched data in cookies, which is accessible on server level
that sounds like a horrendous idea, since this is an entire product object, with possibly hundreds of props ๐Ÿ˜ฎ
but I guess thats a simple way to avoid fetching data multiple times, without using external packages
another way could be using Jotai
hmm, I sometimes miss trpc... that made things a lot easier :/
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**
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
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
@nehalist 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
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
@necm1 its also good practise to always use server components and last choice would be using client components
occasionally there's no way around client components, sadly
I try to do as much as possible on the server
guess thats why they gave us server actions ๐Ÿ˜ญ
found a good example for this case
@necm1 guess thats why they gave us server actions ๐Ÿ˜ญ
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
Original message was deleted
btw. if my suggestion was helpful, would be awesome if you could mark my answer as described here ๐Ÿ˜ญ
โค๏ธ
@nehalist unfortunately this doesn't work, since I can't create a context within server components
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
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
@nehalist and how to invalidate that cache?
Black-throated Blue Warbler
documentation for that should be on the same page if you scroll down a bit ๐Ÿ˜‰
@nehalist and how to invalidate that cache?
Black-throated Blue Warbler
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')
@Black-throated Blue Warbler documentation for that should be on the same page if you scroll down a bit ๐Ÿ˜‰
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.