@@ -40,6 +40,7 @@ export type Values<T extends UseQueryStatesKeysMap> = {
40
40
? NonNullable < ReturnType < T [ K ] [ 'parse' ] > >
41
41
: ReturnType < T [ K ] [ 'parse' ] > | null
42
42
}
43
+ type NullableValues < T extends UseQueryStatesKeysMap > = Nullable < Values < T > >
43
44
44
45
type UpdaterFn < T extends UseQueryStatesKeysMap > = (
45
46
old : Values < T >
@@ -80,7 +81,7 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
80
81
urlKeys = defaultUrlKeys
81
82
} : Partial < UseQueryStatesOptions < KeyMap > > = { }
82
83
) : UseQueryStatesReturn < KeyMap > {
83
- type V = Values < KeyMap >
84
+ type V = NullableValues < KeyMap >
84
85
const stateKeys = Object . keys ( keyMap ) . join ( ',' )
85
86
const resolvedUrlKeys = useMemo (
86
87
( ) =>
@@ -99,6 +100,17 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
99
100
if ( Object . keys ( queryRef . current ) . length !== Object . keys ( keyMap ) . length ) {
100
101
queryRef . current = Object . fromEntries ( initialSearchParams ?. entries ( ) ?? [ ] )
101
102
}
103
+ const defaultValues = useMemo (
104
+ ( ) =>
105
+ Object . fromEntries (
106
+ Object . keys ( keyMap ) . map ( key => [ key , keyMap [ key ] ! . defaultValue ?? null ] )
107
+ ) as Values < KeyMap > ,
108
+ [
109
+ Object . values ( keyMap )
110
+ . map ( ( { defaultValue } ) => defaultValue )
111
+ . join ( ',' )
112
+ ]
113
+ )
102
114
103
115
const [ internalState , setInternalState ] = useState < V > ( ( ) => {
104
116
const source = initialSearchParams ?? new URLSearchParams ( )
@@ -137,7 +149,7 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
137
149
}
138
150
const handlers = Object . keys ( keyMap ) . reduce (
139
151
( handlers , stateKey ) => {
140
- handlers [ stateKey as keyof V ] = ( {
152
+ handlers [ stateKey as keyof KeyMap ] = ( {
141
153
state,
142
154
query
143
155
} : CrossHookSyncPayload ) => {
@@ -147,7 +159,7 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
147
159
// for the subsequent setState to pick it up.
148
160
stateRef . current = {
149
161
...stateRef . current ,
150
- [ stateKey as keyof V ] : state ?? defaultValue ?? null
162
+ [ stateKey as keyof KeyMap ] : state ?? defaultValue ?? null
151
163
}
152
164
queryRef . current [ urlKey ] = query
153
165
debug (
@@ -162,7 +174,7 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
162
174
}
163
175
return handlers
164
176
} ,
165
- { } as Record < keyof V , ( payload : CrossHookSyncPayload ) => void >
177
+ { } as Record < keyof KeyMap , ( payload : CrossHookSyncPayload ) => void >
166
178
)
167
179
168
180
for ( const stateKey of Object . keys ( keyMap ) ) {
@@ -183,7 +195,7 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
183
195
( stateUpdater , callOptions = { } ) => {
184
196
const newState : Partial < Nullable < KeyMap > > =
185
197
typeof stateUpdater === 'function'
186
- ? stateUpdater ( stateRef . current )
198
+ ? stateUpdater ( applyDefaultValues ( stateRef . current , defaultValues ) )
187
199
: stateUpdater === null
188
200
? ( Object . fromEntries (
189
201
Object . keys ( keyMap ) . map ( key => [ key , null ] )
@@ -241,10 +253,16 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
241
253
startTransition ,
242
254
resolvedUrlKeys ,
243
255
updateUrl ,
244
- rateLimitFactor
256
+ rateLimitFactor ,
257
+ defaultValues
245
258
]
246
259
)
247
- return [ internalState , update ]
260
+
261
+ const outputState = useMemo (
262
+ ( ) => applyDefaultValues ( internalState , defaultValues ) ,
263
+ [ internalState , defaultValues ]
264
+ )
265
+ return [ outputState , update ]
248
266
}
249
267
250
268
// --
@@ -254,26 +272,34 @@ function parseMap<KeyMap extends UseQueryStatesKeysMap>(
254
272
urlKeys : Partial < Record < keyof KeyMap , string > > ,
255
273
searchParams : URLSearchParams ,
256
274
cachedQuery ?: Record < string , string | null > ,
257
- cachedState ?: Values < KeyMap >
258
- ) {
275
+ cachedState ?: NullableValues < KeyMap >
276
+ ) : NullableValues < KeyMap > {
259
277
return Object . keys ( keyMap ) . reduce ( ( obj , stateKey ) => {
260
278
const urlKey = urlKeys ?. [ stateKey ] ?? stateKey
261
- const { defaultValue , parse } = keyMap [ stateKey ] !
279
+ const { parse } = keyMap [ stateKey ] !
262
280
const queuedQuery = getQueuedValue ( urlKey )
263
281
const query =
264
282
queuedQuery === undefined
265
283
? ( searchParams ?. get ( urlKey ) ?? null )
266
284
: queuedQuery
267
285
if ( cachedQuery && cachedState && cachedQuery [ urlKey ] === query ) {
268
- obj [ stateKey as keyof KeyMap ] =
269
- cachedState [ stateKey ] ?? defaultValue ?? null
286
+ obj [ stateKey as keyof KeyMap ] = cachedState [ stateKey ] ?? null
270
287
return obj
271
288
}
272
289
const value = query === null ? null : safeParse ( parse , query , stateKey )
273
- obj [ stateKey as keyof KeyMap ] = value ?? defaultValue ?? null
290
+ obj [ stateKey as keyof KeyMap ] = value ?? null
274
291
if ( cachedQuery ) {
275
292
cachedQuery [ urlKey ] = query
276
293
}
277
294
return obj
278
- } , { } as Values < KeyMap > )
295
+ } , { } as NullableValues < KeyMap > )
296
+ }
297
+
298
+ function applyDefaultValues < KeyMap extends UseQueryStatesKeysMap > (
299
+ state : NullableValues < KeyMap > ,
300
+ defaults : Partial < Values < KeyMap > >
301
+ ) {
302
+ return Object . fromEntries (
303
+ Object . keys ( state ) . map ( key => [ key , state [ key ] ?? defaults [ key ] ?? null ] )
304
+ ) as Values < KeyMap >
279
305
}
0 commit comments