Next.js Discord

Discord Forum

redirect() keeps redirecting indefinitely

Answered
PepeW posted this in #help-forum
Open in Discord
I have a product page that looks like the following:

export default async function ProductPage({ params }: { params: { productSlug: string } }) {

  const product = await graphqlServerRequest({
    document: productBySlugQuery,
    variables: { productSlug: params.productSlug },
  })

  ...

  // urlParams = "color=red&size=34"

  redirect(`${params.productSlug}?${urlParams}`)

  return (
    ...
  )
}


I have two problems with this redirect():
1. I get redirected to the good url (http://localhost:3002/example?color=red&size=34) but then it keeps redirecting again and again on the same url
2. It seems like the redirect happens client side because I first see the url without the params (http://localhost:3002/example) and then get redirected to the full url
Answered by B33fb0n3
then you can do it like that:
export default async function ProductPage({ params }: { params: { productSlug: string } }) {

  const product = await ...

  ...

  // calculate the url params based on default variant

  return (
        <CartComponent />
    <SetClientsideURL params={urlParams}/>
    <ProductDesc desc={product.desc} />
    <ProductPic images={product.images}/>
  )
}
View full answer

66 Replies

that's because how you handle the redirect
you fetch stuff and after you fetched it, you redirect. So you see what you saw in your second point: first without then with
so you just need to make the redirect only when a condition is true and then you broke the loop 🙂
Ok i'll try to find a condition to stop the infinite redirect.

About the second point, I don't get how I'm supposed to fix that. Because I need the product to calculate the urlParams
are you able to calc (or only pass) these params to a client component?
yes I can do that why ?
then you can do the following:
your productpage only fetch the product.

the client get's the params and set them as url params (so you don't need to redirect)

because there is no redirect, there will be no loop 🙂
Ok I see

But ideally I would like not see any redirect.
If I request the page http://localhost:3002/example I instantly arrive on the page http://localhost:3002/example?color=red&size=34
yes, there will be NO redirect, but the url will be the "changed"
But because it happens client side, the user will first arrive on the page http://localhost:3002/example then the client component will render and trigger the function to set the urlParams and finally the url will change to http://localhost:3002/example?color=red&size=34 right ?
So the user will see the page "flicker"
page flicker? No, because the user won't be redirected. The url just changes. So the page stays the same and will be rendered only once
When the url is http://localhost:3002/example the page is empty because it needs a default variant.
When the url is http://localhost:3002/example?color=red&size=34 the product can be seen on the page

So if the url change from http://localhost:3002/example to http://localhost:3002/example?color=red&size=34 the user will see a flicker isn't it ? Because the content changes
oh ok, yea, lets fix that. The params will be calculated serverside, right?
Yes
On the page itself
When the user visits http://localhost:3002/example the product will be fetched and the params will be calculated, right?
Yep
ok and your components on your page receives the fetched page data, right?
Here's the full code:
export default async function ProductPage({ params }: { params: { productSlug: string } }) {
  const product= await graphqlServerRequest({
    document: privateSaleProductBySlugQuery,
    variables: { productSlug: params.productSlug },
    onError() {
      notFound()
    },
  })

  // find the default variant
  const defaultVariantCode = product.defaultVariantCode
  const defaultVariant = product.variants?.filter((variant) => variant?.code === defaultVariantCode)[0]
  const defaultVariantFallback = defaultVariant ?? product.variants[0]

// find the options of the default variant
  const optionsOfDefaultVariant = defaultVariantFallback.optionValues?.map((option) => [
    option.option.split("/").slice(-1)[0].toLowerCase(),
    option.value.toLowerCase(),
  ])

  // add the options to the url
  const urlParams = optionsOfDefaultVariant
    ?.map((option, index) => `${option[0]}=${option[1]}${index === optionsOfDefaultVariant.length - 1 ? "" : "&"}`)
    .join("")

  redirect(`${params.productSlug}?${urlParams}`)
then you can do it like that:
export default async function ProductPage({ params }: { params: { productSlug: string } }) {

  const product = await ...

  ...

  // urlParams = "color=red&size=34"

  return (
        <CartComponent />
    <SetClientsideURL params={urlParams}/>
    <ProductDesc desc={product.desc} />
    <ProductPic images={product.images}/>
  )
}

You page will be loaded with your productdata and your product itself and the url will be set by the client. Because the Product Components already loaded and there is no reload (because no redirect), there won't be a flicker
Am I missing something?
Yes ^^

because I can't do product.description for example.

All my product infos comes from a variant and a variant is a combination of options (color=red&size=34 for example)

So the idea would be the following:
1. get to the page http://localhost:3002/example?color=red&size=34
2. find the variant from the options in the url
3. being able to do productVariant.description
Why you don't do it here:
export default async function ProductPage({ params }: { params: { productSlug: string } }) {

  const product = await ...

  ...

  // urlParams = "color=red&size=34"

// HERE ^^^^^^^ there are the params

  return (
...
  )
}
That's what I'm doing but then I need to append urlParams the url
That's why I'm doing a redirect()
But maybe there is another way of appending params to an url
you only need the urlParams to fetch the variant, not the product, right?
So the product can be fetched without them, right?
In the product I already have an array with all the variants (product.variants) so no need to fetch anything more than the product
nice
A product page is always http://localhost:3002/example
and if he enter http://localhost:3002/example they should be redirect to a default variant
We can't Link to the product page with the params before hand
yes
And ideally the redirect should be "invisible"
yea
you fetch the product on http://localhost:3002/example
and also get all the variants with it
yes
so, product is now fetched and variants are fetched
so you have data to display (yea I know: mulitple variants, but you only want to show one... yes, we take a look at that in the next step)
yes
and if the client go on http://localhost:3002/example he should be invisibly redirect to the first variant in (I guess) your variants array or which one?
yes

  // find the default variant
  const defaultVariantCode = product.defaultVariantCode
  const defaultVariant = product.variants?.filter((variant) => variant?.code === defaultVariantCode)[0]
  const defaultVariantFallback = defaultVariant ?? product.variants[0]
the first variant is a fallback but that's the idea
that sounds great. Good job! And then you have data (imagine just for a moment) for the default variant and can display this default variant, right?
Oh you're right
I'm stupid
alright. So the only thing that needs to be done is the invisible url change, right?
I can just pass the defaultVariant as a prop and then client side change the url
but the flicker will not be seen because the content will remain the same
excatly! Or even only pass the new urlParams to the client 🙂
@PepeW but the flicker will not be seen because the content will remain the same
yes. No flicker, same content, only url change
Perfect
Thank you very much for you time 😄
then you can do it like that:
export default async function ProductPage({ params }: { params: { productSlug: string } }) {

  const product = await ...

  ...

  // calculate the url params based on default variant

  return (
        <CartComponent />
    <SetClientsideURL params={urlParams}/>
    <ProductDesc desc={product.desc} />
    <ProductPic images={product.images}/>
  )
}
Answer
yep
cool, please mark solution
Which message ?
^^
done 🙂
Good job 👍