Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scroll on first render of statically generated page with nested child component initialising query state (remote only) #561

Closed
camin-mccluskey opened this issue May 16, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@camin-mccluskey
Copy link

camin-mccluskey commented May 16, 2024

Context

What's your version of nuqs?
"nuqs": "^1.17.4"

  nuqs:
    specifier: ^1.17.4
    version: 1.17.4(next@14.1.4)

Next.js information (obtained by running next info):

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.4.0: Wed Feb 21 21:45:49 PST 2024; root:xnu-10063.101.15~2/RELEASE_ARM64_T6020
Binaries:
  Node: 20.11.0
  npm: 10.2.4
  Yarn: N/A
  pnpm: 8.14.1
Relevant Packages:
  next: 14.1.4
  eslint-config-next: 14.1.0
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A

Are you using:

  • ❌ The app router
  • ✅ The pages router
  • ❌ The basePath option in your Next.js config
  • ❌ The experimental windowHistorySupport flag in your Next.js config

Description

On pages which utilise SSG and which include a child component which calls useQueryState the page is always scrolled on first render. useQueryState does respect the scroll option for subsequent state updates. This issue only occurs upon deployment (i.e. not locally). I'm assuming that SSG is more like sever-side rendering in development.

The problematic child component is wrapped in the following (to solve hydration issues as noted in another issue.

export function ProductPricingSection(props: ProductPricingSectionProps) {
  const isReady = useRouterReady()
  return isReady ? <ProductPricingSectionUnsafe {...props} /> : null
}

Note: This issue is solved by moving the useQueryState initialisation to the page level. This is fine but a little suboptimal as state and state setters now need to be passed to children.

Reproduction

Example: Steps to reproduce the behavior:

  1. Create a statically generated page
  2. Create a child component which makes use of useQueryState
  3. Visit statically generated page post deployment
  4. Observe scroll to element behaviour
@camin-mccluskey camin-mccluskey added the bug Something isn't working label May 16, 2024
@camin-mccluskey camin-mccluskey changed the title Scroll on first render of statically generated page - nested Scroll on first render of statically generated page with nested children component initialising query state (remote only) May 16, 2024
@camin-mccluskey camin-mccluskey changed the title Scroll on first render of statically generated page with nested children component initialising query state (remote only) Scroll on first render of statically generated page with nested child component initialising query state (remote only) May 16, 2024
@franky47
Copy link
Member

Could you please give an example of the contents of <ProductPricingSectionUnsafe> that would lead to this scrolling effect?

What does the URL look like, is there a fragment section? (eg: /foo#bar)

@camin-mccluskey
Copy link
Author

Could you please give an example of the contents of <ProductPricingSectionUnsafe> that would lead to this scrolling effect?

What does the URL look like, is there a fragment section? (eg: /foo#bar)

function ProductPricingSectionUnsafe({ pricing }: ExampleProps ) {  
  const { data: currencyOptionData } = api.pricing.getCurrencyOptions.useQuery({
    pricingId: pricing.id,
  })
  const currencyOptions =
    currencyOptionData?.map(({ currencyCode }) => ({
      value: currencyCode,
      label: currencyCode,
    })) ?? []

 // Problem area
  const [selectedCurrency, setSelectedCurrency] = useQueryState(
    'currency',
    parseAsString.withDefault(currencyOptions?.[0]?.value ?? ''),
  )

  return (
    <Section
      title="Pricing"
      contentRight={
        <div className="flex items-center gap-3">
          <Combobox
            label="Currency"
            buttonLeftIcon={CreditCardIcon}
            options={currencyOptions}
            value={selectedCurrency}
            onChange={(v) => setSelectedCurrency(v as CurrencyCode)}
            searchPlaceholder="Enter a currency"
            className="w-32 min-w-fit"
          />
        </div>
      }
    >
      <ScrollArea>
        <div className="flex w-full gap-3 lg:gap-5">
          {tiers.map((tier) => (
            <PricingTierCard
              key={tier.id}
              tier={tier}
              isCustomPricing={tier.isCustomPricing}
              chargeableUnit={chargeableUnit}
              chargeableUsagePeriod={chargeableUsagePeriod}
              freeTrial={freeTrial}
              preferredCurrency={selectedCurrency as CurrencyCode}
            />
          ))}
        </div>
        <ScrollBar orientation="horizontal" />
      </ScrollArea>
    </Section>
  )
}

export function ProductPricingSection(props: ProductPricingSectionProps) {
  const isReady = useRouterReady()
  return isReady ? <ProductPricingSectionUnsafe {...props} /> : null
}

Url looks like the following - /[id]?currency=GBP (no fragment)

Just wondering now if there is some issue with the fact we will have an undefined default prior to having the options available?

@franky47
Copy link
Member

franky47 commented May 16, 2024

Yes from the look of your example, having the default being populated by an external API call might be the source of the problem.

On initial (post-hydration) render, the default will be an empty string (if no search param is present). Then once your API call resolves, the default value changes, and this is what could cause your view to scroll to the new position.

What if you try mounting the scroll view only after the API call resolves? Does it also solve the scroll problem?

@camin-mccluskey
Copy link
Author

This looks to be solved - thanks so much for the speedy response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants