Skip to content

Commit 072f83f

Browse files
committed
fix: useQueryStates does not update values correctly when config
1 parent 3125a0c commit 072f83f

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// https://github.com/47ng/nuqs/issues/907
2+
3+
'use client'
4+
5+
import { parseAsString, useQueryStates } from 'nuqs'
6+
import { useState } from 'react'
7+
8+
export default function Home() {
9+
const [nuqsConfig, setNuqsConfig] = useState({
10+
p1: parseAsString,
11+
p2: parseAsString
12+
})
13+
14+
const [values] = useQueryStates(nuqsConfig)
15+
16+
return (
17+
<>
18+
<div className="flex gap-2">
19+
<button
20+
onClick={() => setNuqsConfig({ p1: parseAsString })}
21+
className="border p-2"
22+
>
23+
Update config (remove one of the keys)
24+
</button>
25+
26+
<button
27+
onClick={() =>
28+
setNuqsConfig({
29+
p1: parseAsString,
30+
p2: parseAsString,
31+
p3: parseAsString
32+
})
33+
}
34+
className="border p-2"
35+
>
36+
Update config (add a new key)
37+
</button>
38+
39+
<button
40+
onClick={() =>
41+
setNuqsConfig({
42+
p1: parseAsString,
43+
p5: parseAsString
44+
})
45+
}
46+
className="border p-2"
47+
>
48+
Update config (replace a key)
49+
</button>
50+
</div>
51+
<div>Config keys: {JSON.stringify(Object.keys(nuqsConfig))}</div>
52+
<div>Result: {JSON.stringify(values)}</div>
53+
</>
54+
)
55+
}

packages/nuqs/src/useQueryStates.test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,25 @@ describe('useQueryStates: dynamic keys', () => {
346346
expect(result.current[0].d).toEqual(4)
347347
})
348348

349+
it('updating keys also updates the result structure', () => {
350+
const useTestHook = (keys: string[] = ['a', 'b']) =>
351+
useQueryStates(
352+
keys.reduce((acc, key) => ({ ...acc, [key]: parseAsInteger }), {})
353+
)
354+
const { result, rerender } = renderHook(useTestHook, {
355+
wrapper: withNuqsTestingAdapter({
356+
searchParams: ''
357+
})
358+
})
359+
expect(result.current[0]).toStrictEqual({ a: null, b: null })
360+
rerender(['a']) // remove b
361+
expect(result.current[0]).toStrictEqual({ a: null })
362+
rerender(['a', 'b', 'c']) // add c
363+
expect(result.current[0]).toStrictEqual({ a: null, b: null, c: null })
364+
rerender(['a', 'b', 'd']) // remove c, add d
365+
expect(result.current[0]).toStrictEqual({ a: null, b: null, d: null })
366+
})
367+
349368
it('supports dynamic keys with remapping', () => {
350369
const useTestHook = (keys: [string, string] = ['a', 'b']) =>
351370
useQueryStates(

packages/nuqs/src/useQueryStates.ts

+10
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,16 @@ function parseMap<KeyMap extends UseQueryStatesKeysMap>(
310310
}
311311
return out
312312
}, {} as NullableValues<KeyMap>)
313+
314+
if (!hasChanged) {
315+
// check that keyMap keys have not changed
316+
const keyMapKeys = Object.keys(keyMap)
317+
const cachedStateKeys = Object.keys(cachedState ?? {})
318+
hasChanged =
319+
keyMapKeys.length !== cachedStateKeys.length ||
320+
keyMapKeys.some(key => !cachedStateKeys.includes(key))
321+
}
322+
313323
return { state, hasChanged }
314324
}
315325

0 commit comments

Comments
 (0)