47ng / nuqs

Type-safe search params state manager for React frameworks - Like useState, but stored in the URL query string.
https://nuqs.47ng.com
MIT License
4.86k stars 104 forks source link

Return value of `useQueryStates` does not immediately update after update in parsers #760

Closed pdme closed 1 week ago

pdme commented 1 week ago

Context

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

What framework are you using? NextJS

Which version of your framework are you using? 15.0.3

Description

The return value of useQueryStates does not change if the parsers passed into it change.

Reproduction

https://codesandbox.io/p/devbox/vkkl53

Example: Steps to reproduce the behavior:

  1. Go to the codesandbox
  2. Click on FLAG button
  3. See that parsers change but return value not

    -->

franky47 commented 1 week ago

What are you trying to achieve that requires switching the parser type for a given key at runtime?

pdme commented 1 week ago

I want to make the type of parser and its default value dependent on other parameters,. For example the user might trigger a setting that changes the nature of a certain query param, e.g. from single to multiple values, or having a different default value.

I can achieve this by sanitizing/parsing the values after the parsing by useQueryStates, but then I lose the default value behavior (back to null) and is frankly a bit cumbersome. Cleaner would be to be able to update the parsers at runtime...

franky47 commented 1 week ago

You can change the default value dynamically with a variable in .withDefault, however changing the type of parser isn't supported, and would have further side-effects when multiple hooks are listening to the same key. See the Troubleshooting section of the docs.

the user might trigger a setting that changes the nature of a certain query param, e.g. from single to multiple values

What would happen if a user with that setting set to single clicks on a URL set for multiple, and vice versa? This is to highlight that URLs are user-controlled, and that making their meaning as fully-qualified as possible (ie: not dependent on other internal application state) will make them much easier to reason about.

I would suggest using different query keys, each mapping to an associated type, and using redirects when switching settings to rewrite a state key/value pair into another (you can use the serializer helper for this).

pdme commented 1 week ago

You can set the default value dynamically, but only once. You cannot change the default value at runtime, based on external factors (see https://codesandbox.io/p/devbox/vkkl53)...

In any case, thanks for your answer, and a great library in general.

franky47 commented 1 week ago

Ah, now this is indeed a bug with useQueryStates: changing the hook to useQueryState does follow a dynamic default value.

I'll take a look in #762, thanks!

pdme commented 1 week ago

great, thanks so much. I'll take a look myself in the meantime as well, see if I can find the culprit...

franky47 commented 1 week ago

I think it has to do with the cache ref that's used for referential integrity, in useQueryState the default value is nullish-coalesced at the very end when returning the value, but in useQueryStates it's added to the cached state, and that isn't reactive to a change in the default value in the keyMap.

franky47 commented 1 week ago

I've got a fix that passes tests, though I'll run some more to see if it didn't break referential stability as a side-effect.

Can you try the following please?

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

Please note that it only handles dynamic defaults, and not changing parser types at runtime.

pdme commented 1 week ago

this seems to work! great job

github-actions[bot] commented 1 week ago

:tada: This issue has been resolved in version 2.2.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: