Skip to content

Commit e0021b1

Browse files
committed
fix: compareQuery for iterables
1 parent ff10737 commit e0021b1

2 files changed

Lines changed: 43 additions & 5 deletions

File tree

packages/nuqs/src/lib/compare.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export function compareQuery<T extends string | Iterable<string>>(
2+
a: T | null,
3+
b: T | null
4+
): boolean {
5+
if (a === b) {
6+
return true // Referentially stable
7+
}
8+
if (a === null || b === null) {
9+
return false
10+
}
11+
// we expect either strings or iterables, not a mix of both
12+
if (typeof a === 'string' || typeof b === 'string') {
13+
return a === b
14+
}
15+
16+
const iterA = a[Symbol.iterator]()
17+
const iterB = b[Symbol.iterator]()
18+
19+
while (true) {
20+
const nextA = iterA.next()
21+
const nextB = iterB.next()
22+
23+
if (nextA.done && nextB.done) {
24+
return true // both ended at the same time
25+
}
26+
if (nextA.done !== nextB.done) {
27+
return false // different lengths
28+
}
29+
if (nextA.value !== nextB.value) {
30+
return false // mismatched value
31+
}
32+
}
33+
}

packages/nuqs/src/useQueryStates.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { emitter, type CrossHookSyncPayload } from './lib/sync'
1717
import { type Parser } from './parsers'
1818
import { isAbsentFromUrl } from './lib/search-params'
1919
import { safeParse } from './lib/safe-parse'
20+
import { compareQuery } from './lib/compare'
2021

2122
type KeyMapValue<Type> = Parser<Type> &
2223
Options & {
@@ -385,14 +386,18 @@ function parseMap<KeyMap extends UseQueryStatesKeysMap>(
385386
const state = Object.entries(keyMap).reduce((out, [stateKey, parser]) => {
386387
const urlKey = urlKeys?.[stateKey] ?? stateKey
387388
const queuedQuery = queuedQueries[urlKey]
389+
const defaultValue = parser.type === 'multi' ? [] : null
388390
const query =
389391
queuedQuery === undefined
390-
? parser.type === 'multi'
391-
? (searchParams?.getAll(urlKey) ?? [])
392-
: (searchParams?.get(urlKey) ?? null)
392+
? ((parser.type === 'multi'
393+
? searchParams?.getAll(urlKey)
394+
: searchParams?.get(urlKey)) ?? defaultValue)
393395
: queuedQuery
394-
// todo this === comparison likely won't work with arrays
395-
if (cachedQuery && cachedState && (cachedQuery[urlKey] ?? null) === query) {
396+
if (
397+
cachedQuery &&
398+
cachedState &&
399+
compareQuery(cachedQuery[urlKey] ?? defaultValue, query)
400+
) {
396401
// Cache hit
397402
out[stateKey as keyof KeyMap] = cachedState[stateKey] ?? null
398403
return out

0 commit comments

Comments
 (0)