Update search params without re-rendering everything #9851
Replies: 56 comments 26 replies
-
Updating search params is considered navigation, so re-rendering seems appropriate. If you need to maintain state, then I suggest using |
Beta Was this translation helpful? Give feedback.
-
Is there any way to perhaps update it without triggering the re render? With a private API or something. Re working everything to use hash routes is a major effort, not to mention hash params look really ugly. |
Beta Was this translation helpful? Give feedback.
-
Have you looked into https://github.com/remix-run/history/blob/main/docs/api-reference.md#locationstate |
Beta Was this translation helpful? Give feedback.
-
The goal is to actually affect the URL, so let's say if the user refreshes the browser, the component can restore its state from w/e was stored in the URL. Similar idea to allow external links to point to a specific place in the app. I agree this could live in the hash portion of the URL as well, but it is just not friendly to the user or even sharing/bookmarking links. |
Beta Was this translation helpful? Give feedback.
-
Actually, updating the hash part of the location will also cause a router re render:
|
Beta Was this translation helpful? Give feedback.
-
Just curious, but would History API's pushState function help here? With that you can freely change your urls (as long as you don't modify the origin), and on page refresh, react-router will reload based on the contents of that url. |
Beta Was this translation helpful? Give feedback.
-
I did try using the history API, and it works at fist glance. However, react-router ends up out of sync so any code attempting to read from location, queryParams, etc., would break. Not saying it's not possible, as such code could just be updated to read the query string parameters from the history/location API as well instead of react-router, but it's a really bad side effect. |
Beta Was this translation helpful? Give feedback.
-
So, when you update the query params, you also want the relevant hooks (location, query params) to be updated as well? It seems to me like a re-render should then be expected because how else should that data be updated? |
Beta Was this translation helpful? Give feedback.
-
The re renders are considerably slow (compared to v5), and can't even be memoized / used with PureComponents. Here's an example of what we have, maybe it helps with some other ideas (wall of text ahead).
Of course, some components (e.g., tables) are more or less optimized to only re render if items changed, so it is not that bad (still usable without the debug console open). You could say, optimize all your components to only re render if |
Beta Was this translation helpful? Give feedback.
-
I have a similar use-case that I'd like fixed, but with the hashpart. I have an SPA with react-router. In one of the pages, the user can copy links to certain parts of the page like you'd expect with anchors & links, e.g. |
Beta Was this translation helpful? Give feedback.
-
I used this custom hook from @kentcdodds’s personal site https://github.com/kentcdodds/kentcdodds.com/blob/main/app/routes/blog.tsx#L185 for a similar use case. |
Beta Was this translation helpful? Give feedback.
-
@theMosaad that is just like manually calling pushState , which will basically leave react router out of sync if anywhere else you access the query params? |
Beta Was this translation helpful? Give feedback.
-
@cristianoccazinsp as far as i remember, it only rerenders the component and, therefore, react router gives you the new search params when you use it inside that component. |
Beta Was this translation helpful? Give feedback.
-
Same use case (keeping a table's search filters as URL parameters so that the URL can be shared/bookmarked). Except I'm using the provided Note: Tried using setSearchParams option |
Beta Was this translation helpful? Give feedback.
-
Are maintainers willing to accept a PR that fixes this ? Edit : to be more specific, only the components that uses the hook and deconstructed a property that changed would re-render. const [searchParams] = useSearchParams(); // Re-render every time any search param changes
const [{ page }] = useSearchParams(); // Re-render only when `page` changes |
Beta Was this translation helpful? Give feedback.
-
Hi, i think i face the same issue: in a dashboard there are buttons in the lateral drawer that modify the state of dashboard.jsx itself. How can i achieve this behaviour? |
Beta Was this translation helpful? Give feedback.
-
I have a similar problem. I don't mind the components that directly call
EDIT: Reviewing the implementation of the useSearchParams function, this could be avoided by using an additional reference (searchParamsRef):
I'm gonna try replacing the library's useSearchParams function with the above in my own code as a temporary work-around. |
Beta Was this translation helpful? Give feedback.
-
what is the solution |
Beta Was this translation helpful? Give feedback.
-
@0xMohamed This is a known bug with no official fix #7634 |
Beta Was this translation helpful? Give feedback.
-
Somewhat related to this conversation is the
Docs: https://reactrouter.com/en/main/hooks/use-search-params#usesearchparams Somewhere in my code I was updating my URL search params & it looked like it was reloading the page, when actually it was just resetting the scroll position unwanted-reloading.mp4 |
Beta Was this translation helpful? Give feedback.
-
I believe I'm encountering a similar issue (#10741) where updating search parameters doesn't get added to the router history. Consequently, when returning to the page for the first time, scroll restoration doesn't work. If anyone has found a solution for this, I would greatly appreciate any assistance. |
Beta Was this translation helpful? Give feedback.
-
Any update on this? |
Beta Was this translation helpful? Give feedback.
-
I had a similar case, it was necessary for a The way I've solved it is this: /**
* Handles layout changes. We manually update
* the URL in the browser without calling the
* rendering and network request, and `searchParams`
* has this parameter, and takes it into account
* when changing URL parameters elsewhere in the
* component
*/
const onLayoutChange = (layout: 'table' | 'tile') => {
searchParams.set('layout', layout)
window.history.pushState({}, '', '?' + searchParams.toString())
setLayout(layout)
} The point here is this:
I also have an effect: useEffect(() => {
// ... network request if `searchParams` is changed
}, [searchParams]) It doesn't work (it's a wanted effect) because we don't update the state:
|
Beta Was this translation helpful? Give feedback.
-
Hi guys, for me I wrote this middleware like recursive function to ignore specific path of search params query. function addRevalidateToChildren(routes: RouteObject[]): RouteObject[] {
return routes.map(route => {
const children = route.children
? addRevalidateToChildren(route.children)
: undefined;
return {
...route,
children,
shouldRevalidate: revalidateIgnore,
} as RouteObject;
});
}
export const revalidateIgnore: ShouldRevalidateFunction = args => {
return !args.nextUrl.search.includes('search_keyword');
};
// Usage
export const ProtectedRoutes: RouteObject[] = addRevalidateToChildren([
{
element: (
<Suspense fallback={<PageLoadingIndicator />}>
<Outlet />
</Suspense>
),
errorElement: <ErrorPage />,
children: [
{
path: '/',
index: true,
},
{
path: 'users',
loader: ensureAuthenticated,
children: UsersRoutes,
},
],
},
]); |
Beta Was this translation helpful? Give feedback.
-
Use: setSearchParams(params, {preventScrollReset: true} |
Beta Was this translation helpful? Give feedback.
-
We have the same use-case. We have an app which respects state settings supplied as query params, and we want to dynamically update the URL to reflect the state the user sets on the page, so that, for instance, the browser can be refreshed and will come back to the same state. In our case, the state includes which tab is currently selected, and a re-render of the full component tree would include re-rendering hidden tab contents. A re-render of the full component tree doesn't break the state (as the URL is consistent with the page state) but, because the state is already consistent, the re-render is unnecessary and a significant performance issue. I think we can't fix this with memoization. At the moment the only workaround I can see is to use |
Beta Was this translation helpful? Give feedback.
-
Hello, any plans to fix this? Currently this just leads to unnessecary rerenders of my page. |
Beta Was this translation helpful? Give feedback.
-
This seems like a reason to use tanstack router. As that is url-state first, I expect it to not have this bug |
Beta Was this translation helpful? Give feedback.
-
This is the solution I ended up with for my use case: const getHighlightedUids = () => {
return window.location.hash?.split('#')?.[1]?.split(',') ?? [];
};
const useHighlight = (uid: string) => {
const [highlighted, setHighlighted] = useState<boolean>(false);
useEffect(() => {
setHighlighted(getHighlightedUids().includes(uid));
}, [uid]);
return [
highlighted,
() =>
setHighlighted((current) => {
const nextHighlighted = !current;
if (nextHighlighted) {
const nextUids = getHighlightedUids();
nextUids.push(uid);
window.history.replaceState(
null,
'',
window.location.pathname + '#' + nextUids.join(','),
);
} else {
const nextUids = getHighlightedUids().filter(
(currentUid) => currentUid !== uid,
);
window.history.replaceState(
null,
'',
window.location.pathname + '#' + nextUids.join(','),
);
}
return nextHighlighted;
}),
] as const;
}; This approach means that you can instant UI updates. You can try this by clicking any of the ingredients on this page: https://pillser.com/supplements/kal-multisaurus-mixed-berry-407#53,59 |
Beta Was this translation helpful? Give feedback.
-
Technically, this is expected behavior. When the search params change, the loaders re-run since they may be depending on those query params. And that causes re-render. In our case, the fix was to cache data in loaders and memoize our React components in various places to avoid unnecessary re-renders. This is most likely the appropriate fix you're looking for. Everything else proposed here is error prone. |
Beta Was this translation helpful? Give feedback.
-
What is the new or updated feature that you are suggesting?
After migrating from v5 to v6, I noticed some new unexpected re-renders.
In short, the app updates query-string parameters (e.g., selected object id in a table) so if the user reloads the browser, or copies the link, the state is preserved. In version 5, changing the query string values would not cause a re-render from the very first component. In v6, changing the query string (using navigate, or the new useQueryParams hook), will always cause a full re-render from .
Is there a way, or would it be possible to allow for path/query string manipulations without firing a re-render?
Why should this feature be included?
Use cases where query-string manipulation is done only to preserve state, but not to do any actual react state changes.
Beta Was this translation helpful? Give feedback.
All reactions