Next.js Discord

Discord Forum

Good practices for ssr dropdown menus with multiple dialogs

Unanswered
! AlexNotTheLion posted this in #help-forum
Open in Discord
In my nav bar I have a dropdown allowing the user to open multiple dialogs (only 1 dialog open at once) using shadcn, this forces the dropdown container to be a client component because it uses state to store which dialog id should be shown, however inside some of these dialogs are data fetches using suspense, is there a better pattern to achieve this ?

14 Replies

bump
English Spot
ideally no, so i can make proper use of rsc / ssr
but i like being able to use suspense or something like it
i dont like the idea of waterfalling fetched data through client components and want to understand if theres a way around it
currently im doing navbar(server) > dropdown(client) > multiple dialogs(client and data is required here)
bare in mind im rather new to nexjs and im finding it rather challenging to figureout how they demand you structure data fetches, good and bad practices, the difference between fetching on the server vs client etc
also something to bear in mind is all my data fetches are in server actions which make calls to a supabase backend
English Spot
try checking what i sent you, it's server actions basically call server while still inside client component
Will do thanks, I I've been briefly over it before and I'm currently using swr, but are there any drawbacks to using swr Vs ssr server actions?
American Crow
Don't use server actions to get data. They are run sequentially (blocking) .
Not sure if i understand correctly but i think you want to do something like on demand fetching from a client component?
I'd do something like:

page.tsx (server):
 import FetchOnDemand from "./fetch-on-demand"

export default async function Page({ searchParams }) {
   const postToFetch = searchParams.post ?? "1"

   // do not trust searchParams
   // implement a searchParams validation (zod, yup, etc) here

   const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postToFetch}`)
   const post = await response.json()

   return (
      <div>
         <h2>Current Post: {postToFetch}</h2>
         <FetchOnDemand post={post} />
      </div>
   )
}

fetch-on-demand.tsx (client):
"use client"

import { useRouter } from "next/navigation"

export default function FetchOnDemand({ post }) {
   const { replace } = useRouter()

   const handleClick = (value: string) => {
      const params = new URLSearchParams()
      params.set("post", value)
      replace(`?${params.toString()}`)
   }

   return (
      <div>
         <h1>Fetch on Demand</h1>
         <h2>Currently fetched Post: </h2>
         <pre> {JSON.stringify(post, null, 2)} </pre>

         <button
            className="bg-blue-600 px-4 py-2 text-white"
            onClick={() => handleClick("1")}
         >
            fetch post 1 on demand
         </button>
         <button
            className="bg-orange-600 px-4 py-2 text-white"
            onClick={() => handleClick("2")}
         >
            fetch post 2 on demand
         </button>

         <button
            className="bg-purple-600 px-4 py-2 text-white"
            onClick={() => handleClick("3")}
         >
            fetch post 3 on demand
         </button>
      </div>
   )
}
@American Crow Don't use server actions to **get** data. They are run sequentially (blocking) . Not sure if i understand correctly but i think you want to do something like on demand fetching from a client component? I'd do something like: page.tsx (server): tsx import FetchOnDemand from "./fetch-on-demand" export default async function Page({ searchParams }) { const postToFetch = searchParams.post ?? "1" // do not trust searchParams // implement a searchParams validation (zod, yup, etc) here const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postToFetch}`) const post = await response.json() return ( <div> <h2>Current Post: {postToFetch}</h2> <FetchOnDemand post={post} /> </div> ) } fetch-on-demand.tsx (client): tsx "use client" import { useRouter } from "next/navigation" export default function FetchOnDemand({ post }) { const { replace } = useRouter() const handleClick = (value: string) => { const params = new URLSearchParams() params.set("post", value) replace(`?${params.toString()}`) } return ( <div> <h1>Fetch on Demand</h1> <h2>Currently fetched Post: </h2> <pre> {JSON.stringify(post, null, 2)} </pre> <button className="bg-blue-600 px-4 py-2 text-white" onClick={() => handleClick("1")} > fetch post 1 on demand </button> <button className="bg-orange-600 px-4 py-2 text-white" onClick={() => handleClick("2")} > fetch post 2 on demand </button> <button className="bg-purple-600 px-4 py-2 text-white" onClick={() => handleClick("3")} > fetch post 3 on demand </button> </div> ) }
are they sequential even if they're inside a suspense tag ?
for example:
// navbar component thats on the server
<Suspense>
  <ProfileComponent/>
</Suspense>

<Suspense>
  <FriendListComponent/>
</Suspense>

// profile component
export async function Profile(){
  const profile = await getUserProfile();
  return...
}

// friends list component 
export async function FriendsListComponent(){
  const friends = await getFriendsList();
  return...
}
does this run in parallel ?
also im realising i missed this, im accessing supabase via drizzle orm and currently though sever actions