47ng/nuqs

`useQueryState` getter cannot access data on each render

tordans opened this issue · 3 comments

(This issue was #400 first, but that ticket is now about something else, so to keep related things together I moved it here.)

Context

What's your version of next-usequerystate?

    "next-usequerystate": "1.12.0",

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

FYI, I am stuck on this NextJS version due to blitz-js/blitz#4253.

% npx next info

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.6.0: Fri Sep 15 13:41:30 PDT 2023; root:xnu-8796.141.3.700.8~1/RELEASE_ARM64_T8103
Binaries:
  Node: 19.2.0
  npm: 8.19.3
  Yarn: N/A
  pnpm: 8.7.6
Relevant Packages:
  next: 13.5.6
  eslint-config-next: 13.5.6
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

Are you using:

  • ✅ The app router
  • ✖️ pages router // Just for API Routes

Description

My issue is, that the useQuery getters dos not work on every render.

One thing that might be special about this app is, that I initialize the Page in https://github.com/FixMyBerlin/atlas-app/blob/develop/src/app/regionen/%5BregionSlug%5D/_components/MapInterfaceInitialization.tsx#L36-L51 manually. I cannot use .withDefaults for this (or I don't know how…) because I need the current URL state to call initializeMapRegionConfig for same very rough URL migration (see #399 on this topic).

Reproduction

Video
Here is a video which shows the issue in action https://we.tl/t-3JBSbIevOt

Steps

  1. Restart the dev server npm run dev; it only happens reliably on a fresh start (but did not look into this further, yet)
  2. Open https://staging.radverkehrsatlas.de/regionen/berlin
  3. The app starts with initialized=false and correctly sets the states setMapParam and setConfigParam
  4. However, in the following render, the getter for both return undefined
    ## MapInterfaceInitialization rendered {freshConfig: Array(5), urlConfigParam: null, initialized: true, mapParam: null, region: {…}}
    In this log, urlConfigParam and mapParam where just successfully set (or maybe just enqueued?) but are not present.
  5. I then get ## SelectThemes rendered (5) [{…}, {…}, {…}, {…}, {…}] which is correct because it is able to get the url state
  6. Then a lot of logging happens…
  7. And next time ## SelectThemes rendered null the same SelectTheme component is not able to get the url state. The URL itself still holds the state, though, it is just not able to read it.
  8. There are a few renders of ## MapInterfaceInitialization rendered {freshConfig: Array(5), urlConfigParam: null, initialized: true, mapParam: null, region: {…}} afterwards which still show null for the urlConfigParam

(Sorry for the wrong initial title, it was a stale text in the form…)

After François' initial feedback in #400 (comment) I looked into a simpler setup, but the issue still persists:

  • I remove the custom initialization / migration – which does not solve the issue
  • I move the use-hooks in a different component – which does not solve the issue

The new code

https://github.com/FixMyBerlin/atlas-app/pull/51/files/d79dfe61055979e01316749eab067e6a67199956..d5bb3535e4ce54f17f354f833330e1dd8a6a583c

Reproduction

  1. Since I am using the regular .withDefaults now, the first renders are fewer and simpler, now
  2. I am getting ## MapInterface rendered {zoom: 11.8, lat: 52.507, lng: 13.367} (5) [{…}, {…}, {…}, {…}, {…}] which is what I expect
  3. But shortly after, I am the data for mapConfig is lost again ## SelectThemes rendered [] (see the empty [])

Follow up to #404 (comment)

It looks like I am making wrong assumption…

(a) Either about how .withDefaults works: I assumed that the input for withDefaults is only used and needed whenever the given queryparam is empty at the time. Because if it is present, that that will be used and withDefaults hat no effect, right?

However, looking at my current testing at FixMyBerlin/atlas-app@b5642dc#diff-94a946c527a273e63467a5e6ced5fc9f38f159ef0bddff1fafc349ceab225875R5 where I provide the defaults as an optional param it appears as if the []

(b) And/or about how the getters are internally recognized as "being the same getter". I assumed that this is done just by the params, so regardless of the settings I provide, every time I use useQueryState('config',…) I would get the same internal store back. However, that is not correct, is it?

I now got it working in FixMyBerlin/atlas-app@c37c2d6 where I made sure that all places of the code call the useQueryState exactly the same way. This is wasteful in my case, because the DB query to look for the initial data is only used on the first render when no state is present, yet.


With this working solution in FixMyBerlin/atlas-app@c37c2d6 have have a few issues that are not ideal…

  1. I only want to query the DB when no state is present (the useRegion() hook)
  2. I want the URL to initialize with query params right away. Right now, the params only show up once I change stuff that is relevant to the params, but I want the to be visible right a on the second render — Update: I created #405 to discuss this
  3. I want the defaults to be derived from the possible initial URL state – which is what #399 is about

Could you provide a minimal reproducible example in codesandbox please? I try to follow what you are saying but I am lost in the end...I am not a maintainer of this repo but I probability wouldn't want to go through the entire code base to figure out the bug