In the fast-evolving landscape of React development, efficient state management is key to creating seamless user experiences. In this article, we'll explore a custom React hook designed to revolutionize the way you handle multiple states using search params. Get ready to simplify your code, enhance SEO, and elevate your application's performance.
Syncing multiple states to route params or search params is a mess
The struggle is well-known: synchronizing multiple variables from the same component to the url search params can be a pain. If you currently rely on react-router-dom,
it supplies the hook useSearchParams. This hook might look intriguing from the outside, but once you open the docs for this hook you will soon notice that the
interface this hook exposes is...well, let's call it suboptimal. The setter, setSearchParams works by completely overriding all of the currently set search params everytime it is called. Most of the time, this is not the behaviour you are looking for.
Suppose you have the following URL scheme: /page?search=searchterm&tab=list and you'd like to store search and tab as two variables in a useState, you'd need the following code to make things work without any further react libraries:
const [search, setSearch] = useState();
const [tab, setTab] = useState();
useEffect(() => {
const newParams = new URLSearchParams(location.search); // Create object based on current params
newParams.set(search, search);
window.location.search = newParams.toString(); // Update search params
}, [search]);
useEffect(() => {
const newParams = new URLSearchParams(location.search); // Create object based on current params
newParams.set(tab, tab);
window.location.search = newParams.toString(); // Update search params
}, [tab]);
return <>{/* Some stuff that calls the setters defined above */}<>This might look feasible in this specific example, but this solution has one major caveat: The assignment operation in the useEffects will cause a page reload.
That reload will lead to terrible UX and therefore is to be avoided.
Introducing useRouteMultiState
For an overall better UX and simpler DX (by exposing a simpler interface), you can use my useRouteMultiState Hook. This hook allows you to simply store multiple variables in the URL search params and have them act like react state, e.g. you can
listen to changes to those variables in useEffect hooks. This hook represents what setSearchParams from react-router-dom should have been, in my opinion.
How to handle multiple react states in URL search params
Sticking with the example mentioned above, we have two string: search and tab. With my useRouteMultiState hook, storing those two variables in search params is as simple as the following example:
const [routeState, setRouteState] = useRouteMultiState({
template: "/category/:categoryId",
keys: ["categoryId"],
defaults: { categoryId: "1" },
});
const search = routeState?.["searchParams"]?.["search"];
const setSeach = (newVal) => {
setRouteState({ searchParams: { search: newVal } });
};
const tab = routeState?.["searchParams"]?.["tab"];
const setTab = (newVal) => {
setRouteState({ searchParams: { tab: newVal } });
};
const currentCategory = routeState["categoryId"];
const setCurrentCategory = useCallback(
(newVal) => setRouteState({ categoryId: newVal }),
[setRouteState],
);
return <>{/* JSX nodes */}</>;As you can see, we define a route template (using the templating style from react-router-dom) and define an array of keys we want to manage using the hook, based on the url scheme. The hook returns the state and a setter for this state. For convenience, we can then create a handful of wrapper functions and variables that reference the state. In the end we have 3 convenient variables which we can use in furhter hooks, or just render. On top of that we receive three setters, which come in handy for setting the correct value in the URL. No more assignments or building the URL string by hand!
Conclusion
useRouteMultiState provides a simple, convenient interface to parameters stored in the URL in your React application. The only requirement this hook has is that you're already
using react-router-dom in your React project. Storing multiple values and syncing them with the URL has never before been so easy! You can grab the useRouteMultiState Hook from my GitHub. Happy state-keeping!