Skip to content

Commit

Permalink
feat: Allow clearing all search params managed by useQueryStates (#622)
Browse files Browse the repository at this point in the history
* feat: Allow clearing all search params managed by useQueryStates

By passing `null` instead of an object, every key managed
by that`useQueryStates` hook will be cleared from the URL.
  • Loading branch information
franky47 authored Sep 1, 2024
1 parent 099ceb3 commit 4c335a5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 12 deletions.
13 changes: 13 additions & 0 deletions packages/docs/content/docs/batching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,16 @@ setCoordinates({
```

The order of precedence is: call-level options > parser options > global options.

<Callout title="Tip">
You can clear all keys managed by a `useQueryStates` hook by passing
`null` to the state updater function.

```ts
const clearAll = () => setCoordinates(null)
```

This will clear `lat` & `lng`, and leave other search params untouched.

</Callout>

8 changes: 8 additions & 0 deletions packages/e2e/cypress/e2e/useQueryStates-clear-all.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// <reference types="cypress" />

it('useQueryStates clear all', () => {
cy.visit('/app/useQueryStates-clear-all?a=foo&b=bar')
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
cy.get('button').click()
cy.location('search').should('eq', '')
})
24 changes: 24 additions & 0 deletions packages/e2e/src/app/app/useQueryStates-clear-all/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'

import { parseAsString, useQueryStates } from 'nuqs'
import { Suspense } from 'react'

export default function Page() {
return (
<Suspense>
<Client />
</Suspense>
)
}

function Client() {
const [, setValues] = useQueryStates({
a: parseAsString.withDefault(''),
b: parseAsString.withDefault('')
})
return (
<>
<button onClick={() => setValues(null)}>Clear</button>
</>
)
}
7 changes: 2 additions & 5 deletions packages/nuqs/src/tests/compat/useQueryStates.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expectError, expectNotAssignable, expectType } from 'tsd'
import { expectNotAssignable, expectType } from 'tsd'
import { queryTypes, useQueryStates } from '../../../dist'

{
Expand Down Expand Up @@ -50,10 +50,7 @@ import { queryTypes, useQueryStates } from '../../../dist'
hasDefault: null,
doesNot: null
}))
// but not at root level
expectError(() => {
setStates(null)
})
setStates(null)
}

// Custom parsers
Expand Down
7 changes: 2 additions & 5 deletions packages/nuqs/src/tests/useQueryStates.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expectError, expectNotAssignable, expectType } from 'tsd'
import { expectNotAssignable, expectType } from 'tsd'
import {
parseAsBoolean,
parseAsFloat,
Expand Down Expand Up @@ -57,10 +57,7 @@ import {
hasDefault: null,
doesNot: null
}))
// but not at root level
expectError(() => {
setStates(null)
})
setStates(null)
}

// Custom parsers
Expand Down
8 changes: 6 additions & 2 deletions packages/nuqs/src/useQueryStates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type UpdaterFn<T extends UseQueryStatesKeysMap> = (
) => Partial<Nullable<Values<T>>>

export type SetValues<T extends UseQueryStatesKeysMap> = (
values: Partial<Nullable<Values<T>>> | UpdaterFn<T>,
values: Partial<Nullable<Values<T>>> | UpdaterFn<T> | null,
options?: Options
) => Promise<URLSearchParams>

Expand Down Expand Up @@ -140,7 +140,11 @@ export function useQueryStates<KeyMap extends UseQueryStatesKeysMap>(
const newState: Partial<Nullable<KeyMap>> =
typeof stateUpdater === 'function'
? stateUpdater(stateRef.current)
: stateUpdater
: stateUpdater === null
? (Object.fromEntries(
Object.keys(keyMap).map(key => [key, null])
) as Nullable<KeyMap>)
: stateUpdater
debug('[nuq+ `%s`] setState: %O', keys, newState)
for (let [key, value] of Object.entries(newState)) {
const parser = keyMap[key]
Expand Down

0 comments on commit 4c335a5

Please sign in to comment.