Skip to content

feat(use-query-params): add ability to push instead of replacing current history #355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/use-query-params/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<h1>User: {user}</h1>
<h1>Company: {company}</h1>
<button onClick={setUser}>Set User John</button>
</>
)
}
```
2 changes: 1 addition & 1 deletion packages/use-query-params/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
44 changes: 39 additions & 5 deletions packages/use-query-params/src/__tests__/index.tsx
Original file line number Diff line number Diff line change
@@ -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 }) =>
(
<MemoryRouter initialIndex={0} initialEntries={[{ pathname, search }]}>
{children}
</MemoryRouter>
history ? (
<Router history={history}>
{children}
</Router>
) : (
<MemoryRouter initialIndex={0} initialEntries={[{ pathname, search }]}>
{children}
</MemoryRouter>
)
)

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' }),
Expand Down
46 changes: 28 additions & 18 deletions packages/use-query-params/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | number | boolean>;
replaceQueryParams: (newParams: Record<string, unknown>) => void;
setQueryParams: (nextParams: Record<string, unknown>) => 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<History>()
const { replace, push } = useHistory<History>()
// eslint-disable-next-line @typescript-eslint/unbound-method
const location = useLocation<History>()

Expand All @@ -35,35 +52,28 @@ const useQueryParams = (): {
)

const replaceInUrlIfNeeded = useCallback(
newState => {
(newState: Record<string,unknown>, 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<string,unknown>): void => {
replaceInUrlIfNeeded({ ...currentState, ...nextParams })
(nextParams: Record<string,unknown>, 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<string,unknown>): void => {
replaceInUrlIfNeeded({ ...newParams })
(newParams: Record<string,unknown>, options?: Options): void => {
replaceInUrlIfNeeded(newParams, options)
},
[replaceInUrlIfNeeded],
)
Expand Down
9 changes: 1 addition & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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==
Expand Down Expand Up @@ -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"
Expand Down