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

Dynamic keys in useQueryStates #799

Open
erinleigh90 opened this issue Dec 10, 2024 · 9 comments · May be fixed by #858
Open

Dynamic keys in useQueryStates #799

erinleigh90 opened this issue Dec 10, 2024 · 9 comments · May be fixed by #858
Labels
bug Something isn't working feature/useQueryStates
Milestone

Comments

@erinleigh90
Copy link

erinleigh90 commented Dec 10, 2024

Context

What's your version of nuqs?

-> "nuqs": "^2.2.3",

What framework are you using?

  • ✅ Next.js (app router)

Which version of your framework are you using?

-> "next": "14.2.20",

Description

We need expected queryParams to be populated dynamically from retrieved data. However, when the queryStates are populated after the relevant data is fetched, pre-existing queryParams in the url are not applied to the new queryStates. I don't know if this would technically count as a bug or a feature request, but either way I need a way to capture the initial state of queryParams once our filters have been populated.

Originally posted here: #531

Reproduction

Minimal Reproduction:
https://github.com/erinleigh90/nuqs-dynamic-query-states

Steps to reproduce the behavior:

  1. Go to 'http://localhost:3000/?language=English'
  2. Observe queryStates Language is null
  3. Check any filter option
  4. Observe queryStates values have been updated with the new filter value
@erinleigh90 erinleigh90 added the bug Something isn't working label Dec 10, 2024
@franky47 franky47 added the adapters/next/app Uses the Next.js app router label Dec 10, 2024
@franky47 franky47 added this to the 🪵 Backlog milestone Dec 10, 2024
@franky47
Copy link
Member

franky47 commented Dec 10, 2024

I see, what you want is a dynamic set of keys in the object passed to useQueryStates, which doesn't seem to be supported at the moment, but I agree that it feels like it should.

I'll see if it's doable without causing a ton of re-renders in normal use-cases (due to that object often being declared inline and so being recreated on each render).

@franky47 franky47 added feature New feature or request and removed bug Something isn't working labels Dec 10, 2024
@franky47 franky47 changed the title NUQS not populating query state for dynamically populated parameters Dynamic keys in useQueryStates Dec 10, 2024
@franky47 franky47 added bug Something isn't working and removed feature New feature or request labels Dec 10, 2024
@erinleigh90
Copy link
Author

@franky47 Thank you so much for looking into it!

@franky47 franky47 removed the adapters/next/app Uses the Next.js app router label Jan 3, 2025
@taymoor89
Copy link

This library is excellent, but it requires URLSearchParams to be known in advance, which is unfortunately a deal breaker for our use case. While the reasoning behind this is understandable, I hope it will support supplying a dynamic object in the future.

@franky47
Copy link
Member

franky47 commented Jan 7, 2025

@taymoor89 would you be able to share more about your use-case?

The idea for dynamic keys would be that:

  1. useQueryStates can pick up changes in the keys of the object passed as argument
  2. But not changes in the parsers themselves (the values in that object)

The reason being that it would be non type-safe to do point n°2: dynamic states like this would not be representable statically. Also, for performance reasons, detecting changes in the parsers (which are not referentially stable across renders when defined inline, which is usually the way they are specified, and the only way to do this dynamically) would cause a lot of undesired re-renders.

That would mean that all parsers under dynamic keys would have to be the same.

@yarinsa
Copy link

yarinsa commented Jan 13, 2025

Joining the discussion here. I enabled nuqs debug mode and identified the issue. Although we maintain a state for the ‘parsers’ and update useQueryStates accordingly, the subscription to the parameters occurs at a later stage. This delay causes the subsequent setState calls to be missed by nuqs, resulting in the state being treated as external.

Here’s a simplified onChange function example that replicates the issue:

const { parsers, setParsers } = useState(initialParsers);
const [state, setState] = useQueryStates(parsers);

const onChange = (newState: Array<{id: string, value: string}>) => {
  const nextParsers =  Object.fromEntries(newState.map(i => [i.id, parseAsString]))
  // Update parsers first

  setParsers(() => nextParsers);

  // Then update state
  setState(() => newState); //Doesn't do anything (for new fields, works on a second render of a field)
};

In this case, calling setParsers before setState doesn’t work as expected because nuqs doesn’t re-subscribe to the updated parsers. Attempts to use setTimeout or useTransition doesn't help, as these do not guarantee synchronization of subscriptions with nuqs.

@franky47
Copy link
Member

franky47 commented Jan 13, 2025

Could y'all try [email protected] and let me know if it works better for your use-cases? I did fix one of the useEffect dependencies to re-sync on the right keys, so it should pick up state from new keys (after re-rendering, as it's in a useEffect).

@yarinsa, regarding setting the parsers and state at the same time: you will need to wait for the setParsers call to re-render and get picked up by useQueryStates, which will update the setState function reference, then you can call the new reference.

@erinleigh90
Copy link
Author

@franky47 Thanks for looking into it! Unfortunately I'm still seeing the same behavior using [email protected]

@erinleigh90
Copy link
Author

We were able get around this issue by making sure that the component where the queryParams are defined is not rendered until the underlying data is loaded. This won't help if you need the queryParams to change after the initial params are fetched, but it does work for our scenario.

@franky47 franky47 linked a pull request Jan 13, 2025 that will close this issue
1 task
@franky47
Copy link
Member

franky47 commented Jan 13, 2025

I found the issue about why reading the values didn't sync with key updates (it was a classic cache invalidation issue).

Could you please try this preview build and let me know how it goes? @erinleigh90 it seems to pass your repro.

pnpm add https://pkg.pr.new/nuqs@858

Note that the values will still be picked up on the next render after the keys have been updated.

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

Successfully merging a pull request may close this issue.

4 participants