-
-
Notifications
You must be signed in to change notification settings - Fork 154
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
useSearchParams
for extracting and modifying search parameters
#368
Comments
I was also facing the issue of extracting search params. But I have built a custom hook for that. I can raise a pull request for it. |
Hi! Sorry, this isn't well documented (yet! PRs are welcome). You can subscribe to search string updates via: import { useSearch, useLocationProperty, navigate } from 'wouter/use-location';
// get all search params:
const search = useSearch(); Note that you will have to parse these using |
@molefrog Actually the issue isn't getting the search params, it's actually updating them. We need a hook like useSearchParams of React Router Dom, to retrieve as well as update the search params. |
I see, this isn't provided out-of-the-box right now. But I'll try to hack a simple implementation for that. |
I've written the So far it has been working great for me, hope it helps :)
import { useCallback, useMemo, useRef } from 'react';
import { navigate, useSearch } from 'wouter/use-location';
// Based on react-router: https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/index.tsx
type ParamKeyValuePair = [string, string];
type URLSearchParamsInit =
| string
| ParamKeyValuePair[]
| Record<string, string | string[]>
| URLSearchParams;
export function createSearchParams(
init: URLSearchParamsInit = '',
): URLSearchParams {
return new URLSearchParams(
typeof init === 'string' ||
Array.isArray(init) ||
init instanceof URLSearchParams
? init
: Object.keys(init).reduce((memo, key) => {
const value = init[key];
return memo.concat(
Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]],
);
}, [] as ParamKeyValuePair[]),
);
}
export function getSearchParamsForLocation(
locationSearch: string,
defaultSearchParams: URLSearchParams | null,
) {
const searchParams = createSearchParams(locationSearch);
if (defaultSearchParams) {
// Use `defaultSearchParams.forEach(...)` here instead of iterating of
// `defaultSearchParams.keys()` to work-around a bug in Firefox related to
// web extensions. Relevant Bugzilla tickets:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1414602
// https://bugzilla.mozilla.org/show_bug.cgi?id=1023984
defaultSearchParams.forEach((_, key) => {
if (!searchParams.has(key)) {
defaultSearchParams.getAll(key).forEach((value) => {
searchParams.append(key, value);
});
}
});
}
return searchParams;
}
export function useSearchParams(defaultInit?: URLSearchParamsInit) {
if (typeof URLSearchParams === 'undefined') {
console.warn(
`You cannot use the \`useSearchParams\` hook in a browser that does not ` +
`support the URLSearchParams API. If you need to support Internet ` +
`Explorer 11, we recommend you load a polyfill such as ` +
`https://github.com/ungap/url-search-params\n\n` +
`If you're unsure how to load polyfills, we recommend you check out ` +
`https://polyfill.io/v3/ which provides some recommendations about how ` +
`to load polyfills only for users that need them, instead of for every ` +
`user.`,
);
}
const defaultSearchParamsRef = useRef(createSearchParams(defaultInit));
const hasSetSearchParamsRef = useRef(false);
const search = useSearch();
const searchParams = useMemo(
() =>
// Only merge in the defaults if we haven't yet called setSearchParams.
// Once we call that we want those to take precedence, otherwise you can't
// remove a param with setSearchParams({}) if it has an initial value
getSearchParamsForLocation(
search,
hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current,
),
[search],
);
const setSearchParams = useCallback(
(
nextInit:
| URLSearchParamsInit
| ((prev: URLSearchParams) => URLSearchParamsInit),
navigateOpts?: Parameters<typeof navigate>['1'],
) => {
const newSearchParams = createSearchParams(
typeof nextInit === 'function' ? nextInit(searchParams) : nextInit,
);
hasSetSearchParamsRef.current = true;
navigate('?' + newSearchParams, navigateOpts);
},
[searchParams],
);
return [searchParams, setSearchParams] as const;
}
export function App() {
const [searchParams, setSearchParams] = useSearchParams();
return (
<main>
<p>{searchParams.get('key')}</p>
<button onClick={() => setSearchParams({ key: 'value' })}>
Set Param
</button>
<button
onClick={() =>
setSearchParams((prevSearchParams) => ({
...Object.fromEntries(prevSearchParams),
array: ['value_1', 'value_2'], // Array is supported just like react-router.
}))
}
>
Merge Params
</button>
</main>
);
} If interested, I could open up a PR to add this as an official helper util :) |
Cool, that could be a nice feature to have in v3. We're preparing the first release candidate right now, I will let you know once it is ready, so you can start working on a PR. |
The RC is out now. There is no |
useSearchParams
for extracting search parameters
useSearchParams
for extracting search parametersuseSearchParams
for extracting and modifying search parameters
I've created a PR for this feature based on my understanding (I am no expert in Routing 👀). |
i find it quite often that you might want to replace searchParam but you don't really want to trigger a re-render in same component! useSearch will trigger re-render everytime; infact current implementaion trigger re-render even if params/href didnot change at all. (it re-render on any navigation event) a nice js helper to manipulate searchParams is more than enough more most cases imo
|
I believe triggering a re-render upon May I know on what occasion that you would want to update the search params without re-rendering? This also mean that when you hit back on the browser, nothing happens because UI does not re-render. |
Need something like useSearchParams in React Router to handle queries.
The text was updated successfully, but these errors were encountered: