Skip to content

Commit

Permalink
fix: query params hook (#85)
Browse files Browse the repository at this point in the history
* fix: effect cleanup debounce like

* fix: rebased

* fix: tests and add possibility to replace

* feat: split methods
  • Loading branch information
DorianMaliszewski authored Mar 22, 2021
1 parent 2bb4742 commit 848ba10
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 45 deletions.
7 changes: 5 additions & 2 deletions packages/use-query-params/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"reactjs",
"hooks",
"params",
"react-router-dom",
"query-params"
],
"main": "dist/index.js",
Expand All @@ -26,11 +27,13 @@
},
"license": "MIT",
"dependencies": {
"query-string": "^6.14.1"
"query-string": "^6.14.1",
"react-router-dom": "^5.2.0"
},
"peerDependencies": {
"react": "17.x",
"react-dom": "17.x"
"react-dom": "17.x",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.9",
Expand Down
76 changes: 66 additions & 10 deletions packages/use-query-params/src/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { act, renderHook } from '@testing-library/react-hooks'
import React from 'react'
import { MemoryRouter } from 'react-router-dom'
import useQueryParam from '../index'

// eslint-disable-next-line react/prop-types
const wrapper = ({ pathname = 'one', search }) => ({ children }) => {
window.history.replaceState(
window.history.state,
null,
`${pathname}?${search}`,
)

return children
}
const wrapper = ({ pathname = 'one', search }) => ({ children }) => (
<MemoryRouter initialIndex={0} initialEntries={[{ pathname, search }]}>
{children}
</MemoryRouter>
)

describe('useQueryParam', () => {
it('should set one object', () => {
Expand Down Expand Up @@ -117,7 +115,7 @@ describe('useQueryParam', () => {
})
})

it('should correctly set different objects before rerender', async () => {
it('should correctly set different objects before rerender', () => {
const { result, rerender } = renderHook(() => useQueryParam(), {
wrapper: wrapper({ search: '' }),
})
Expand Down Expand Up @@ -150,4 +148,62 @@ describe('useQueryParam', () => {
test: 'Scaleway',
})
})

test('should render good params with parallel changes', async () => {
jest.useFakeTimers()
const { result } = renderHook(() => useQueryParam(), {
wrapper: wrapper({ search: '' }),
})
act(() => {
result.current.setQueryParams({ name: 'John' })
result.current.setQueryParams({ lastName: 'Doe' })
result.current.setQueryParams({ compagny: 'Scaleway' })
})

jest.runAllTimers()

expect(result.current.queryParams).toEqual({
name: 'John',
lastName: 'Doe',
compagny: 'Scaleway',
})

act(() => {
result.current.setQueryParams({ name: 'John' })
})

jest.runAllTimers()

expect(result.current.queryParams).toEqual({
name: 'John',
lastName: 'Doe',
compagny: 'Scaleway',
})
})

test('should erase params', () => {
const { result } = renderHook(() => useQueryParam(), {
wrapper: wrapper({ search: '' }),
})

act(() => {
result.current.setQueryParams({ name: 'John' })
})
expect(result.current.queryParams).toEqual({
name: 'John',
})
act(() => {
result.current.setQueryParams({ lastName: 'Doe' })
})
expect(result.current.queryParams).toEqual({
name: 'John',
lastName: 'Doe',
})
act(() => {
result.current.replaceQueryparams({ compagny: 'Scaleway' })
})
expect(result.current.queryParams).toEqual({
compagny: 'Scaleway',
})
})
})
72 changes: 49 additions & 23 deletions packages/use-query-params/src/index.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,65 @@
import { parse, stringify } from 'query-string'
import { useCallback, useEffect, useState } from 'react'
import { useHistory } from 'react-router-dom'

const parseFormat = search =>
parse(search, {
parseNumbers: true,
parseBooleans: true,
arrayFormat: 'comma',
})
const useQueryParams = () => {
const {
location: { search, pathname },
replace,
} = useHistory()

const stringyFormat = params =>
stringify(params, {
arrayFormat: 'comma',
sort: (a, b) => a.localeCompare(b),
})
const parseFormat = useCallback(
() =>
parse(search, {
parseNumbers: true,
parseBooleans: true,
arrayFormat: 'comma',
}),
[search],
)

const useQueryParams = () => {
const { search, pathname } = window.location
const stringyFormat = useCallback(
params =>
stringify(params, {
arrayFormat: 'comma',
sort: (a, b) => a.localeCompare(b),
}),
[],
)

const [state, setState] = useState(parseFormat(search))
const [state, setState] = useState(parseFormat())

const setQueryParams = useCallback(nextParams => {
/**
* 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 = nextParams => {
setState(prevState => ({ ...prevState, ...nextParams }))
}, [])
}

/**
* Replace the query params in the url. It erase all current values and put the new ones
* @param {Object} newParams
*/
const replaceQueryparams = newParams => {
setState({ ...newParams })
}

useEffect(() => {
const stringifiedParams = stringyFormat(state)
window.history.replaceState(
window.history.state,
null,
`${pathname}?${stringifiedParams}`,
)
}, [pathname, state])
const handler = setTimeout(() => {
const stringifiedParams = stringyFormat(state)
if (search !== `?${stringifiedParams}`)
replace(`${pathname}?${stringifiedParams}`)
}, 500)

return () => {
clearTimeout(handler)
}
}, [pathname, replace, state, search, stringyFormat])

return {
queryParams: state,
replaceQueryparams,
setQueryParams,
}
}
Expand Down
Loading

0 comments on commit 848ba10

Please sign in to comment.