DEV Community

Cover image for Simplifying React Router Query Parameter Management with Custom Hooks
Mayank vishwakarma
Mayank vishwakarma

Posted on

Simplifying React Router Query Parameter Management with Custom Hooks

Managing query parameters in a React application can be challenging, especially when you need to synchronize them with your component's state.

React Router provides hooks like useLocation and useSearchParams to help with this, but managing them together can still be complex.

In this blog post, we'll introduce an enhanced custom hook, useQueryParams, that simplifies the management of query parameters and adds a new feature to remove query parameters from the URL.

Introducing useQueryParams Custom Hook

The useQueryParams custom hook combines the functionality of useLocation, useSearchParams, and adds the ability to manage query parameters more effectively. It provides four main functionalities:

  1. Accessing All Query Parameters: Easily access all query parameters and their values as an object.
  2. Accessing a Specific Query Parameter: Get the value of a specific query parameter by its key.
  3. Updating Query Parameters: Update the value of a query parameter, which automatically updates the browser's URL.
  4. Removing Query Parameters: Remove a query parameter from the URL by its key.

Hook Implementation:

Create a useQueryParams.ts file in src/hooks folder:

import { useEffect, useState } from "react";
import { useLocation, useSearchParams } from "react-router-dom";

export default function useQueryParams() {
  const location = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();
  const [allQueryParams, setAllQueryParams] = useState(Object.fromEntries(searchParams));

  useEffect(() => {
    setAllQueryParams(Object.fromEntries(searchParams));
  }, [searchParams]);

  const getQueryParamByKey = (key: string) => {
    const params = new URLSearchParams(location.search);
    return params.get(key) || "";
  };

  const setQueryParam = (key :string, value: string) => {
    const params = new URLSearchParams(location.search);
    params.set(key, value.toString());
    setSearchParams(params.toString());
  };

  const removeQueryParamByKey = (key: string) => {
    const params = new URLSearchParams(location.search);
    params.delete(key);
    setSearchParams(params.toString());
  };

  return {
    allQueryParams,
    getQueryParamByKey,
    setQueryParam,
    removeQueryParamByKey,
  };
}

Enter fullscreen mode Exit fullscreen mode

How to Use useQueryParams Hook

To use the useQueryParams hook, follow these steps:

  • Import the Hook:
import useQueryParams from './hooks/useQueryParams';
Enter fullscreen mode Exit fullscreen mode
  • Accessing All Query Parameters:
const { allQueryParams } = useQueryParams();
Enter fullscreen mode Exit fullscreen mode

This gives you an object containing all query parameters and their values.

  • Accessing a Specific Query Parameter:
const searchTerm = getQueryParamByKey('search');
const currentPage = getQueryParamByKey('page');
Enter fullscreen mode Exit fullscreen mode

This retrieves the value of a specific query parameter by its key.

  • Updating Query Parameters:
setQueryParam('search', 'react hooks');
setQueryParam('page', 2);
Enter fullscreen mode Exit fullscreen mode

This updates the value of a query parameter, which will reflect in the browser's URL.

  • Removing Query Parameters:
removeQueryParamByKey('page');
Enter fullscreen mode Exit fullscreen mode

This removes the page query parameter from the URL.

Example Usage

Here's a simple example demonstrating the usage of the useQueryParams hook in a React component:

App.tsx

import { ChangeEvent, useEffect, useState } from 'react';
import useQueryParams from './hooks/useQueryParams';

function App() {
  const { allQueryParams, getQueryParamByKey, setQueryParam, removeQueryParamByKey } =
    useQueryParams();

  const [input, setInput] = useState<string>(getQueryParamByKey('search'));
  const [page, setPage] = useState<number>(Number(getQueryParamByKey('page')));

  useEffect(() => {
    setQueryParam('search', input);
  }, [input]);

  useEffect(() => {
    setQueryParam('page', page.toString());
  }, [page]);

  return (
    <div>
      <h1>All Query Params:</h1>
      <pre>{JSON.stringify(allQueryParams)}</pre>
      <h1>Query Param by Key:</h1>
      <span>Search Box : </span>
      <input
        type="text"
        value={input}
        onChange={(event: ChangeEvent<HTMLInputElement>) => {
          setInput(event.target.value);
        }}
      />
      <div>
        <button
          onClick={() => {
            page > 0 && setPage(page - 1);
          }}
        >
          {'<'}
        </button>
        <span> Current Page: {getQueryParamByKey('page')} </span>
        <button
          onClick={() => {
            setPage(page + 1);
          }}
        >
          {'>'}
        </button>
      </div>
      <h1> Remove query param </h1>
      <button
          onClick={() => {
            removeQueryParamByKey('page')
          }}
        >Revome page
        </button>
        <button
          onClick={() => {
            removeQueryParamByKey('search')
          }}
        >Remove search
        </button>

    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

To use the useQueryParams custom hook with React Router, make sure you wrap your application in a BrowserRouter component. This is required for the useLocation and useSearchParams hooks to work correctly. Here's how you can add the BrowserRouter to your application:

main.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import { BrowserRouter, Route, Routes } from 'react-router-dom';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path={'/'} element={<App />} />
      </Routes>
    </BrowserRouter>
  </React.StrictMode>
);

Enter fullscreen mode Exit fullscreen mode

Conclusion

Managing query parameters in a React application can be simplified with the useQueryParams custom hook. By encapsulating the logic for accessing, updating, and removing query parameters, this hook provides a cleaner and more maintainable approach to handling query parameters in your React components. Try it out in your next project and streamline your query parameter management!

Happy Coding 😊!

Top comments (4)

Collapse
 
aminnairi profile image
Amin

What if the query parameter is mispelled or not found in the current URL? It would be great to have an optional fallback value as it may increase the size of the code to create a condition inside the useState just for this check.

Also, you could have saved some typings by using a useMemo instead of a useState + useEffect.

const [allQueryParams, setAllQueryParams] = useState(Object.fromEntries(searchParams));

  useEffect(() => {
    setAllQueryParams(Object.fromEntries(searchParams));
  }, [searchParams]);
Enter fullscreen mode Exit fullscreen mode

We could turn this code into the following.

const [allQueryParams, setAllQueryParams] = useMemo(() => {
  return Object.fromEntries(searchParams);
}, [searchParams]);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mayankvishwakarmadev profile image
Mayank vishwakarma • Edited

Hello @aminnairi
Yes, you can make your custom hook more readable and efficient by using useMemo instead of useState + useEffectand by adding an optional fallback value for the query parameters. Here's how to apply these recommendations:

Adding Optional Fallback Value:

By adding a second parameter to the getQueryParamByKey method, you may offer an extra fallback value for query parameters. If the query parameter is misspelled or cannot be located, the fallback value will be sent back.
Here's the updated getQueryParamByKey function:

const getQueryParamByKey = (key, fallbackValue = '') => {
  const params = new URLSearchParams(location.search);
  return params.get(key) || fallbackValue;
};
Enter fullscreen mode Exit fullscreen mode

Now, you can provide a fallback value when calling getQueryParamByKey:

const searchTerm = getQueryParamByKey('search', 'defaultSearchValue');
const currentPage = getQueryParamByKey('page', '1');
Enter fullscreen mode Exit fullscreen mode

Using useMemo for Efficiency:

To use useMemo instead of useState + useEffect for initializing allQueryParams, you can do the following:

const allQueryParams = useMemo(() => {
  return Object.fromEntries(searchParams);
}, [searchParams]);

Enter fullscreen mode Exit fullscreen mode

By incorporating these changes, your custom hook will be more efficient and flexible.

Collapse
 
skipperhoa profile image
Hòa Nguyễn Coder

Great, but I find the code a bit long. You can try RTK Query + useMemo.

Collapse
 
mayankvishwakarmadev profile image
Mayank vishwakarma

Hello @skipperhoa

Thank you for your suggestion!
I agree that using RTK Query and useMemo can be a great optimization, especially for larger applications.

However, for beginners, I've opted for a simpler approach using useState and useEffect to manage query parameters. This approach is more beginner-friendly and easier to understand, making it a great starting point for those new to React development.

Feel free to modify the code as needed for your own projects!