Skip to content

Commit eea5b3a

Browse files
feat: ✨ remove empty query parameters by default from the query parameters
1 parent 7c1c7e4 commit eea5b3a

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

src/query-parameter-store.test.tsx

+84
Original file line numberDiff line numberDiff line change
@@ -787,4 +787,88 @@ describe('createQueryParamStore', () => {
787787
})
788788
})
789789
})
790+
791+
it('removes empty parameters by default', async () => {
792+
// Arrange
793+
const { useQueryParams } = createQueryParamStore(schema)
794+
const { result } = renderHook(() => useQueryParams(), {
795+
wrapper: MemoryRouterProvider,
796+
})
797+
798+
// Act
799+
act(() => {
800+
result.current[1]({
801+
search: '',
802+
filters: [],
803+
page: undefined,
804+
})
805+
})
806+
807+
// Assert
808+
await waitFor(() => {
809+
expect(mockRouter.query).toEqual({})
810+
expect(result.current[0]).toEqual({})
811+
})
812+
})
813+
814+
it('keeps empty parameters when keepEmptyParams is true', async () => {
815+
// Arrange
816+
const { useQueryParams } = createQueryParamStore(schema)
817+
const { result } = renderHook(() => useQueryParams(), {
818+
wrapper: MemoryRouterProvider,
819+
})
820+
821+
// Act
822+
act(() => {
823+
result.current[1](
824+
{
825+
search: '',
826+
filters: [],
827+
page: undefined,
828+
},
829+
{ keepEmptyParameters: true },
830+
)
831+
})
832+
833+
// Assert
834+
await waitFor(() => {
835+
expect(mockRouter.query).toEqual({
836+
search: '',
837+
filters: [],
838+
})
839+
expect(result.current[0]).toEqual({
840+
search: '',
841+
filters: [],
842+
})
843+
})
844+
})
845+
846+
it('preserves non-empty parameters while removing empty ones', async () => {
847+
// Arrange
848+
const { useQueryParams } = createQueryParamStore(schema)
849+
const { result } = renderHook(() => useQueryParams(), {
850+
wrapper: MemoryRouterProvider,
851+
})
852+
853+
// Act
854+
act(() => {
855+
result.current[1]({
856+
search: 'test',
857+
filters: [],
858+
page: 1,
859+
})
860+
})
861+
862+
// Assert
863+
await waitFor(() => {
864+
expect(mockRouter.query).toEqual({
865+
search: 'test',
866+
page: 1,
867+
})
868+
expect(result.current[0]).toEqual({
869+
search: 'test',
870+
page: 1,
871+
})
872+
})
873+
})
790874
})

src/query-parameter-store.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface SetRouterQueryParamsOptions {
1313
scroll?: boolean
1414
replace?: boolean
1515
logErrorsToConsole?: boolean
16+
keepEmptyParameters?: boolean
1617
}
1718

1819
export type SetQueryParamOptions = SetRouterQueryParamsOptions & {
@@ -29,6 +30,21 @@ interface QueryParamStore<P> {
2930
resetInitialization: () => void
3031
}
3132

33+
const filterEmptyValues = (obj: Record<string, any>): Record<string, any> => {
34+
return Object.entries(obj).reduce(
35+
(acc, [key, value]) => {
36+
const isEmpty =
37+
value === undefined || value === null || value === '' || (Array.isArray(value) && value.length === 0)
38+
39+
if (!isEmpty) {
40+
acc[key] = value
41+
}
42+
return acc
43+
},
44+
{} as Record<string, any>,
45+
)
46+
}
47+
3248
const createStore = <P extends z.ZodType>(
3349
schema: P,
3450
routerOptions: SetRouterQueryParamsOptions = {},
@@ -146,10 +162,15 @@ const createStore = <P extends z.ZodType>(
146162
...dynamicParams,
147163
} as z.infer<P>
148164

165+
const finalParams =
166+
options.keepEmptyParameters || routerOptions.keepEmptyParameters
167+
? mergedParams
168+
: filterEmptyValues(mergedParams)
169+
149170
pushOrReplace(
150171
{
151172
pathname: options.pathname || Router.pathname,
152-
query: mergedParams,
173+
query: finalParams,
153174
},
154175
undefined,
155176
pick(

0 commit comments

Comments
 (0)