diff --git a/packages/use-query-params/README.md b/packages/use-query-params/README.md
index 070187f45..1548e3dfa 100644
--- a/packages/use-query-params/README.md
+++ b/packages/use-query-params/README.md
@@ -86,3 +86,28 @@ const Component = () => {
)
}
```
+
+### Push onto the stack instead of replacing
+
+To avoid mutating history
+
+```js
+// In this exemple we assume that we have an URL that include : `?company=Scaleway".
+import React from 'react'
+import useQueryParams from '@scaleway/use-query-params'
+
+const Component = () => {
+ const { queryParams, replaceQueryParams } = useQueryParams()
+ const { user, company } = queryParams // user will be undefined and company will be "Scaleway"
+ const setUser = () => replaceQueryParams({ user: 'John' }, { push: true }) // user will be "John" and company will be undefined
+ // ?user=John
+
+ return (
+ <>
+
User: {user}
+
Company: {company}
+
+ >
+ )
+}
+```
diff --git a/packages/use-query-params/package.json b/packages/use-query-params/package.json
index 534de51fc..517895b33 100644
--- a/packages/use-query-params/package.json
+++ b/packages/use-query-params/package.json
@@ -28,7 +28,7 @@
},
"license": "MIT",
"dependencies": {
- "history": "^5.0.0",
+ "history": "^4.9.0",
"query-string": "^7.0.0",
"react-router-dom": "^5.2.0"
},
diff --git a/packages/use-query-params/src/__tests__/index.tsx b/packages/use-query-params/src/__tests__/index.tsx
index 8af706244..8c6e87df3 100644
--- a/packages/use-query-params/src/__tests__/index.tsx
+++ b/packages/use-query-params/src/__tests__/index.tsx
@@ -1,19 +1,53 @@
import { act, renderHook } from '@testing-library/react-hooks'
+import { History, createMemoryHistory } from 'history'
import React, { ReactNode } from 'react'
-import { MemoryRouter } from 'react-router-dom'
+import { MemoryRouter, Router } from 'react-router-dom'
import useQueryParams from '..'
const wrapper =
- ({ pathname = 'one', search }: { pathname?: string, search: string }) =>
+ ({ pathname = 'one', search, history }: { pathname?: string, search: string, history?: History }) =>
// eslint-disable-next-line react/prop-types
({ children }: { children: ReactNode }) =>
(
-
- {children}
-
+ history ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ )
)
describe('useQueryParam', () => {
+ it('should correctly push instead of replacing history', () => {
+ const history = createMemoryHistory({
+ initialEntries: ['user=john'],
+ initialIndex: 0,
+ })
+
+ const { result } = renderHook(() => useQueryParams(), {
+ wrapper: wrapper({ history, search: '' }),
+ })
+
+ act(() => {
+ result.current.setQueryParams({ user: 'John' })
+ })
+ expect(result.current.queryParams).toEqual({ user: 'John' })
+ expect(history.length).toBe(1)
+ act(() => {
+ result.current.setQueryParams({ user: 'Jack' })
+ })
+ expect(result.current.queryParams).toEqual({ user: 'Jack' })
+ expect(history.length).toBe(1)
+ act(() => {
+ result.current.setQueryParams({ user: 'Jerry' }, { push: true })
+ })
+ expect(result.current.queryParams).toEqual({ user: 'Jerry' })
+ expect(history.length).toBe(2)
+ })
+
it('should set one object', () => {
const { result } = renderHook(() => useQueryParams(), {
wrapper: wrapper({ search: 'user=john' }),
diff --git a/packages/use-query-params/src/index.ts b/packages/use-query-params/src/index.ts
index 8e1b616d5..9d963f66e 100644
--- a/packages/use-query-params/src/index.ts
+++ b/packages/use-query-params/src/index.ts
@@ -3,13 +3,30 @@ import { ParsedQuery, parse, stringify } from 'query-string'
import { useCallback, useMemo } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
+interface Options {
+ /** Set to true to push a new entry onto the history stack */
+ push: boolean
+}
+
const useQueryParams = (): {
queryParams: ParsedQuery;
- replaceQueryParams: (newParams: Record) => void;
- setQueryParams: (nextParams: Record) => void;
+ /**
+ * Replace the query params in the url. It erase all current values and put the new ones
+ *
+ * @param newParams - The values to set in the query string, overweriting existing one
+ * @param options - Options to define behavior
+ */
+ replaceQueryParams: typeof replaceQueryParams
+ /**
+ * Set query params in the url. It merge the existing values with the new ones.
+ *
+ * @param nextParams - The values to add or update in the existing query string
+ * @param options - Options to define behavior
+ */
+ setQueryParams: typeof setQueryParams
} => {
// eslint-disable-next-line @typescript-eslint/unbound-method
- const { replace } = useHistory()
+ const { replace, push } = useHistory()
// eslint-disable-next-line @typescript-eslint/unbound-method
const location = useLocation()
@@ -35,35 +52,28 @@ const useQueryParams = (): {
)
const replaceInUrlIfNeeded = useCallback(
- newState => {
+ (newState: Record, options?: Options) => {
const stringifiedParams = stringyFormat(newState)
const searchToCompare = location.search || '?'
if (searchToCompare !== `?${stringifiedParams}`) {
- replace(`${location.pathname}?${stringifiedParams}`)
+ const fn = options?.push ? push : replace
+ fn(`${location.pathname}?${stringifiedParams}`)
}
},
- [replace, location.pathname, location.search, stringyFormat],
+ [push, replace, location.pathname, location.search, stringyFormat],
)
- /**
- * Set query params in the url. It merge the existing values with the new ones.
- * @param {Object} nextParams The params to set in the url as query params
- */
const setQueryParams = useCallback(
- (nextParams: Record): void => {
- replaceInUrlIfNeeded({ ...currentState, ...nextParams })
+ (nextParams: Record, options?: Options): void => {
+ replaceInUrlIfNeeded({ ...currentState, ...nextParams }, options)
},
[currentState, replaceInUrlIfNeeded],
)
- /**
- * Replace the query params in the url. It erase all current values and put the new ones
- * @param {Object} newParams
- */
const replaceQueryParams = useCallback(
- (newParams: Record): void => {
- replaceInUrlIfNeeded({ ...newParams })
+ (newParams: Record, options?: Options): void => {
+ replaceInUrlIfNeeded(newParams, options)
},
[replaceInUrlIfNeeded],
)
diff --git a/yarn.lock b/yarn.lock
index c27fed89e..99ecca533 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -997,7 +997,7 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
-"@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":
+"@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":
version "7.15.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
@@ -4732,13 +4732,6 @@ history@^4.9.0:
tiny-warning "^1.0.0"
value-equal "^1.0.1"
-history@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/history/-/history-5.0.1.tgz#de35025ed08bce0db62364b47ebbf9d97b5eb06a"
- integrity sha512-5qC/tFUKfVci5kzgRxZxN5Mf1CV8NmJx9ByaPX0YTLx5Vz3Svh7NYp6eA4CpDq4iA9D0C1t8BNIfvQIrUI3mVw==
- dependencies:
- "@babel/runtime" "^7.7.6"
-
hoist-non-react-statics@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"