Skip to content

Commit 5b09201

Browse files
committed
feat(use-query-params): add ability to push instead of replacing current history
1 parent b585339 commit 5b09201

File tree

2 files changed

+69
-23
lines changed

2 files changed

+69
-23
lines changed

packages/use-query-params/src/__tests__/index.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,55 @@
11
import { act, renderHook } from '@testing-library/react-hooks'
2+
import { History, createMemoryHistory } from 'history'
23
import React, { ReactNode } from 'react'
3-
import { MemoryRouter } from 'react-router-dom'
4+
import { MemoryRouter, Router } from 'react-router-dom'
45
import useQueryParams from '..'
56

67
const wrapper =
7-
({ pathname = 'one', search }: { pathname?: string, search: string }) =>
8+
({ pathname = 'one', search, history }: { pathname?: string, search: string, history?: History }) =>
89
// eslint-disable-next-line react/prop-types
910
({ children }: { children: ReactNode }) =>
1011
(
11-
<MemoryRouter initialIndex={0} initialEntries={[{ pathname, search }]}>
12-
{children}
13-
</MemoryRouter>
12+
history ? (
13+
<Router history={history}>
14+
{children}
15+
</Router>
16+
) : (
17+
<MemoryRouter initialIndex={0} initialEntries={[{ pathname, search }]}>
18+
{children}
19+
</MemoryRouter>
20+
)
1421
)
1522

1623
describe('useQueryParam', () => {
24+
it('should correctly push instead of replacing history', () => {
25+
const history = createMemoryHistory({
26+
initialEntries: ['user=john'],
27+
initialIndex: 0,
28+
})
29+
30+
const { result } = renderHook(() => useQueryParams(), {
31+
wrapper: wrapper({ history, search: '' }),
32+
})
33+
34+
act(() => {
35+
result.current.setQueryParams({ user: 'John' })
36+
})
37+
expect(result.current.queryParams).toEqual({ user: 'John' })
38+
expect(history.length).toBe(1)
39+
act(() => {
40+
result.current.setQueryParams({ user: 'Jack' })
41+
})
42+
expect(result.current.queryParams).toEqual({ user: 'Jack' })
43+
expect(history.length).toBe(1)
44+
act(() => {
45+
result.current.setQueryParams({ user: 'Jerry' }, { push: true })
46+
})
47+
expect(result.current.queryParams).toEqual({ user: 'Jerry' })
48+
expect(history.length).toBe(2)
49+
50+
console.log(history)
51+
})
52+
1753
it('should set one object', () => {
1854
const { result } = renderHook(() => useQueryParams(), {
1955
wrapper: wrapper({ search: 'user=john' }),

packages/use-query-params/src/index.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,30 @@ import { ParsedQuery, parse, stringify } from 'query-string'
33
import { useCallback, useMemo } from 'react'
44
import { useHistory, useLocation } from 'react-router-dom'
55

6+
interface Options {
7+
/** Set to true to push a new entry onto the history stack */
8+
push: boolean
9+
}
10+
611
const useQueryParams = (): {
712
queryParams: ParsedQuery<string | number | boolean>;
8-
replaceQueryParams: (newParams: Record<string, unknown>) => void;
9-
setQueryParams: (nextParams: Record<string, unknown>) => void;
13+
/**
14+
* Replace the query params in the url. It erase all current values and put the new ones
15+
*
16+
* @param newParams - The values to set in the query string, overweriting existing one
17+
* @param options - Options to define behavior
18+
*/
19+
replaceQueryParams: typeof replaceQueryParams
20+
/**
21+
* Set query params in the url. It merge the existing values with the new ones.
22+
*
23+
* @param nextParams - The values to add or update in the existing query string
24+
* @param options - Options to define behavior
25+
*/
26+
setQueryParams: typeof setQueryParams
1027
} => {
1128
// eslint-disable-next-line @typescript-eslint/unbound-method
12-
const { replace } = useHistory<History>()
29+
const { replace, push } = useHistory<History>()
1330
// eslint-disable-next-line @typescript-eslint/unbound-method
1431
const location = useLocation<History>()
1532

@@ -35,35 +52,28 @@ const useQueryParams = (): {
3552
)
3653

3754
const replaceInUrlIfNeeded = useCallback(
38-
newState => {
55+
(newState: Record<string,unknown>, options?: Options) => {
3956
const stringifiedParams = stringyFormat(newState)
4057
const searchToCompare = location.search || '?'
4158

4259
if (searchToCompare !== `?${stringifiedParams}`) {
43-
replace(`${location.pathname}?${stringifiedParams}`)
60+
const fn = options?.push ? push : replace
61+
fn(`${location.pathname}?${stringifiedParams}`)
4462
}
4563
},
46-
[replace, location.pathname, location.search, stringyFormat],
64+
[push, replace, location.pathname, location.search, stringyFormat],
4765
)
4866

49-
/**
50-
* Set query params in the url. It merge the existing values with the new ones.
51-
* @param {Object} nextParams The params to set in the url as query params
52-
*/
5367
const setQueryParams = useCallback(
54-
(nextParams: Record<string,unknown>): void => {
55-
replaceInUrlIfNeeded({ ...currentState, ...nextParams })
68+
(nextParams: Record<string,unknown>, options?: Options): void => {
69+
replaceInUrlIfNeeded({ ...currentState, ...nextParams }, options)
5670
},
5771
[currentState, replaceInUrlIfNeeded],
5872
)
5973

60-
/**
61-
* Replace the query params in the url. It erase all current values and put the new ones
62-
* @param {Object} newParams
63-
*/
6474
const replaceQueryParams = useCallback(
65-
(newParams: Record<string,unknown>): void => {
66-
replaceInUrlIfNeeded({ ...newParams })
75+
(newParams: Record<string,unknown>, options?: Options): void => {
76+
replaceInUrlIfNeeded(newParams, options)
6777
},
6878
[replaceInUrlIfNeeded],
6979
)

0 commit comments

Comments
 (0)