Skip to content

Instantly share code, notes, and snippets.

@mkhoussid
Created June 26, 2024 12:11
Show Gist options
  • Save mkhoussid/d6cb58fb9eba048212329c1d090cebf4 to your computer and use it in GitHub Desktop.
Save mkhoussid/d6cb58fb9eba048212329c1d090cebf4 to your computer and use it in GitHub Desktop.
useSearchParams (adapted from react-router/-dom)
/*
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