Created
June 26, 2024 12:11
-
-
Save mkhoussid/d6cb58fb9eba048212329c1d090cebf4 to your computer and use it in GitHub Desktop.
useSearchParams (adapted from react-router/-dom)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I wanted to update and listen to search param changes without installing any more libraries than neccessary. | |
So, instead of installing `react-router-dom` just for its `useSearchParams` hook, I plucked some of their code. | |
Their `useSearchParams` hook used `useNavigate` to update the url, and `LocationContext` wraps | |
their `Routes` component, so I omitted those two as well | |
*/ | |
import * as React from 'react'; | |
import { SetSearchParams, SearchParamsChange } from 'interfaces/SearchParams'; | |
import { ParamKeyValuePair, SearchParamsChange } from 'interfaces/SearchParams'; | |
export type ParamKeyValuePair = [string, string]; | |
export type SearchParamsChange = URLSearchParams | string; | |
export type SetSearchParams = (change?: (prev: URLSearchParams) => SearchParamsChange) => void; | |
export class SearchParamsHelper { | |
public static createSearchParams(init: SearchParamsChange = '') { | |
return new URLSearchParams( | |
typeof init === 'string' || Array.isArray(init) || init instanceof URLSearchParams | |
? init | |
: Object.keys(init).reduce((memo, key) => { | |
let value = init[key] as string[]; | |
return memo.concat( | |
Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]], | |
); | |
}, [] as ParamKeyValuePair[]), | |
); | |
} | |
public static getSearchParamsForLocation( | |
locationSearch: string, | |
defaultSearchParams: URLSearchParams | null, | |
) { | |
const searchParams = this.createSearchParams(locationSearch); | |
if (defaultSearchParams) { | |
defaultSearchParams.forEach((_, key) => { | |
if (!searchParams.has(key)) { | |
defaultSearchParams.getAll(key).forEach((value) => { | |
searchParams.append(key, value); | |
}); | |
} | |
}); | |
} | |
return searchParams; | |
} | |
} | |
export const useSearchParams = ( | |
defaultInit?: SearchParamsChange, | |
): [URLSearchParams, SetSearchParams] => { | |
const defaultSearchParamsRef = React.useRef(SearchParamsHelper.createSearchParams(defaultInit)); | |
const didSetSearchParamsRef = React.useRef(false); | |
const [dependency, reinvokeSearchParams] = React.useReducer((x) => x + 1, 0); | |
const searchParams = React.useMemo( | |
() => | |
SearchParamsHelper.getSearchParamsForLocation( | |
window.location.search, | |
didSetSearchParamsRef.current ? null : defaultSearchParamsRef.current, | |
), | |
[dependency, window.location.href], | |
); | |
const setSearchParams = React.useCallback<SetSearchParams>( | |
(change) => { | |
const newSearchParams = SearchParamsHelper.createSearchParams(change(searchParams)); | |
didSetSearchParamsRef.current = true; | |
const path = [ | |
window.location.origin, | |
window.location.pathname, | |
'?', | |
String(newSearchParams), | |
].join(''); | |
instead_of_useNavigate: { | |
window.history.pushState(null, null, path); | |
reinvokeSearchParams(); | |
} | |
}, | |
[searchParams], | |
); | |
return [searchParams, setSearchParams]; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment