Next.js Discord

Discord Forum

Loader causes server component to render twice?

Answered
Bedlington Terrier posted this in #help-forum
Open in Discord
Bedlington TerrierOP
Facing a problem when my server component are creating and fetching some data in my postgre database using prisma. Whenever I send the user to my page (the server component) the page is rendered twice, which makes double posts in database. This only occur when I'm using the Loader.tsx component, if I remove the loader it works fine, but of course I dont get a loader.
Answered by Bedlington Terrier
After updating to 14.2 the problem is now solved!
View full answer

44 Replies

Bedlington TerrierOP
@Bedlington Terrier Click to see attachment
Facing a problem when my server component are creating and fetching some data in my postgre database using prisma. Whenever I send the user to my page (the server component) the page is rendered twice, which makes double posts in database. This only occur when I'm using the Loader.tsx component, if I remove the loader it works fine, but of course I dont get a loader.

So I'm sending the user to the new page with a redirect: redirect(/submit-logo-page?

logoPrompts=${JSON.stringify(response.logoPrompts as LogoPrompts)}`);


Then the server component is rendered twice. That component looks something like this:
export default async function SubmitPage({
  params,
  searchParams,
}: {
  params: { slug: string };
  searchParams?: { [key: string]: string | string[] | undefined };
}) {
  let logoPrompts: LogoPrompts;
  if (searchParams !== undefined) {
    logoPrompts = JSON.parse(searchParams.logoPrompts as string);
    await getLogoGenerations();
  } else {
    redirect("/");
  }

  async function getLogoGenerations() {
    const prisma = new PrismaClient();

     //New generation in generation table
     const newGeneration = await prisma.generation.create({
      data: {
        prompt: JSON.stringify(logoPrompts),
        userId: session.user?.id,
        dateTime: new Date(),
        purchaseMade: false,
      },
    });
    generationId = newGeneration.id;
    
    //This generation id
    console.log("Generation_id: " + generationId);

    return (
      <div>
      <h1 className="text-2xl mb-4">Your generated logos</h1>
        <div className="grid grid-cols-fluid gap-12">
          {imageGenerations &&
            imageGenerations.map((imageGeneration: ImageGeneration) => {
              return (
                <Suspense key={imageGeneration.id} fallback={<Loading />}>
                    <LogoPurchaseCard
                      key={imageGeneration.id}
                      imageUrl={imageGeneration.b64WM}
                      generationId={imageGeneration.generationId}
                      id={imageGeneration.id}
                    />
                </Suspense>
              );
            })}
        </div>
    )
}
Okay now I ican read it lol
I dont see a loader.tsx component in the code you provided?
Bedlington TerrierOP
Sorry, its just:
export default function loading() {
    console.log('loading!');
    
  return (
    <div>loading...</div>
  )
}
 
and where do you put that?
Sorry, I cant fix a problem I cant see lol
Bedlington TerrierOP
In the same folder as the server component (page.tsx)
Nah, I mean wherre in the code you provided do you use the loading.tsx?
Your giving us like fragments of the issue, but not the whole issue.
Bedlington TerrierOP
In the suspense in the return as you see above:
<div>
      <h1 className="text-2xl mb-4">Your generated logos</h1>
        <div className="grid grid-cols-fluid gap-12">
          {imageGenerations &&
            imageGenerations.map((imageGeneration: ImageGeneration) => {
              return (
                <Suspense key={imageGeneration.id} fallback={<Loading />}>
                    <LogoPurchaseCard
                      key={imageGeneration.id}
                      imageUrl={imageGeneration.b64WM}
                      generationId={imageGeneration.generationId}
                      id={imageGeneration.id}
                    />
                </Suspense>
              );
            })}
        </div>


 
In the fallback={}
I should add that this only occurs one the FIRST load on the new page I'm being redirected to. If I reload the same page it only occurs once, as it should be
Gotcha, im recreating a min viable page with your code lol just a min
I feel like your doing this in a very weird way.
@Jboncz I feel like your doing this in a very weird way.
Bedlington TerrierOP
That could be very true 😄
import { Suspense } from "react";

async function getLogoGenerations() {
  const imageGenerations = [{ id: 'abc123' }, { id: 'cba321' }]

  console.log('loading the thing man')

  return (
    <div className="grid grid-cols-fluid gap-12">
      {imageGenerations &&
        imageGenerations.map((imageGeneration) => {
          return (
            <Suspense key={imageGeneration.id} fallback={<>loading</>}>
              <>Done loading</>
              <br />
            </Suspense>
          );
        })}
    </div>
  )
}


export default async function SubmitPage({ params, searchParams }) {
  let logoPrompts;
  if (searchParams !== undefined) {
    const data = await getLogoGenerations();
    return data
  }
  else {
    redirect("/");
  }
}
Doing it like this... IU only get one 'loading the thing man'
obviously I cant emulate everything your doing.
I dont have a prisma db or anything.
Bedlington TerrierOP
I see. So we break out the "getLogoGEnerations() from the page default function basically?
I mean... I dont think you gotta, but it looks way cleaner like this.
Idk for sure if that fixes your issue or not, I cannot replicate your issue unfortunately.
Give it a shot and see what happens.
as we say at work... whats the worst that can happen
Bedlington TerrierOP
I see. I'll give it a try. Took a screenshot on the behavior in the server-componentin the meantime:
wait
your mapping over an array, so the array has to have 2 items in it in that case?
imageGenerations.map((imageGeneration) => {

}))
That makes sense to have 2 generation_ids no?
Bedlington TerrierOP
hmm.. This code should only run one time
    //New generation in generation table
    const newGeneration = await prisma.generation.create({
      data: {
        prompt: JSON.stringify(logoPrompts),
        userId: session.user?.id,
        dateTime: new Date(),
        purchaseMade: false,
      },
    });
    generationId = newGeneration.id;

    //This generation id
    console.log("Generation_id: " + generationId);


But I think you're up to something. I should investigate why it's being 2 of them in the database
@Jboncz That makes sense to have 2 generation_ids no?
Bedlington TerrierOP
That shouldn't make sense becuase we're only suppose to create one new row
So wait, where does "imageGenerations " variable come from?
Bedlington TerrierOP
It's another table where I'm using the
generationId = newGeneration.id;

to find information from another table
Alright, so you should scale this back. One thing at a time, find exactly where your issue is. Im having a hard time following because I cant replicate you issue to much DB stuff happening for me to be able to replicate it.
I have to go to the store, be back in a bit and ill look more.
Bedlington TerrierOP
I think my problem is related to an auth session const session = await getServerSession(authOptions);
So gonna investigate more on that
Bedlington TerrierOP
After reading up on the issue I actually believe it's this bug I'm seeing https://github.com/vercel/next.js/issues/59800
Bedlington TerrierOP
It seemed to be fixed in Next 14.2, but I have to upgrade and test it out first. 14.1 currently
Bedlington TerrierOP
So my problem is that i use server functions to redirect to another page, which can cause this bug with a loader. This is a client component which is a Form. When submitting the form I want to change page, clearly my solution isn't the best, any help would be appreciated 🙂
Bedlington TerrierOP
After updating to 14.2 the problem is now solved!
Answer