Skip to content

Commit 2628127

Browse files
feat(use-query-params): add ability to push instead of replacing current history (#355)
* feat(use-query-params): add ability to push instead of replacing current history * docs: update readme * fix: use correct history version * Update packages/use-query-params/README.md Co-authored-by: Adrien Gibrat <[email protected]> Co-authored-by: Adrien Gibrat <[email protected]>
1 parent 95fa09b commit 2628127

File tree

5 files changed

+94
-32
lines changed

5 files changed

+94
-32
lines changed

packages/use-query-params/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,28 @@ const Component = () => {
8686
)
8787
}
8888
```
89+
90+
### Push onto the stack instead of replacing
91+
92+
To avoid mutating history
93+
94+
```js
95+
// In this exemple we assume that we have an URL that include : `?company=Scaleway".
96+
import React from 'react'
97+
import useQueryParams from '@scaleway/use-query-params'
98+
99+
const Component = () => {
100+
const { queryParams, replaceQueryParams } = useQueryParams()
101+
const { user, company } = queryParams // user will be undefined and company will be "Scaleway"
102+
const setUser = () => replaceQueryParams({ user: 'John' }, { push: true }) // user will be "John" and company will be undefined
103+
// ?user=John
104+
105+
return (
106+
<>
107+
<h1>User: {user}</h1>
108+
<h1>Company: {company}</h1>
109+
<button onClick={setUser}>Set User John</button>
110+
</>
111+
)
112+
}
113+
```

packages/use-query-params/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"license": "MIT",
3030
"dependencies": {
31-
"history": "^5.0.0",
31+
"history": "^4.9.0",
3232
"query-string": "^7.0.0",
3333
"react-router-dom": "^5.2.0"
3434
},

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

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,53 @@
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+
1751
it('should set one object', () => {
1852
const { result } = renderHook(() => useQueryParams(), {
1953
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
)

yarn.lock

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@
997997
core-js-pure "^3.0.0"
998998
regenerator-runtime "^0.13.4"
999999

1000-
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
1000+
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
10011001
version "7.15.3"
10021002
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
10031003
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
@@ -4732,13 +4732,6 @@ history@^4.9.0:
47324732
tiny-warning "^1.0.0"
47334733
value-equal "^1.0.1"
47344734

4735-
history@^5.0.0:
4736-
version "5.0.1"
4737-
resolved "https://registry.yarnpkg.com/history/-/history-5.0.1.tgz#de35025ed08bce0db62364b47ebbf9d97b5eb06a"
4738-
integrity sha512-5qC/tFUKfVci5kzgRxZxN5Mf1CV8NmJx9ByaPX0YTLx5Vz3Svh7NYp6eA4CpDq4iA9D0C1t8BNIfvQIrUI3mVw==
4739-
dependencies:
4740-
"@babel/runtime" "^7.7.6"
4741-
47424735
hoist-non-react-statics@^3.1.0:
47434736
version "3.3.2"
47444737
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"

0 commit comments

Comments
 (0)