JAVASCRIPT

Synchronize React State with URL Query Parameters

Learn to build a `useQueryParamsSync` hook to read and write React state directly to URL query parameters, enabling shareable and bookmarkable filtered views.

import { useState, useEffect, useCallback } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; // Assuming react-router-dom v6+

function useQueryParamsSync(key, defaultValue) {
  const location = useLocation();
  const navigate = useNavigate();

  const queryParams = new URLSearchParams(location.search);

  const [value, setValue] = useState(() => {
    const paramValue = queryParams.get(key);
    if (paramValue) {
      // Attempt to parse if it might be non-string (e.g., number, boolean, JSON)
      try {
        return JSON.parse(paramValue);
      } catch (e) {
        return paramValue; // If JSON parsing fails, treat as string
      }
    }
    return defaultValue;
  });

  // Effect to update URL when state changes
  useEffect(() => {
    const currentParams = new URLSearchParams(location.search);
    const serializedValue = typeof value === 'string' ? value : JSON.stringify(value);

    if (serializedValue !== String(currentParams.get(key))) { // Compare serialized values
      if (serializedValue !== String(defaultValue)) { // Only set if different from default
        currentParams.set(key, serializedValue);
      } else {
        currentParams.delete(key); // Remove if value matches default
      }
      navigate(`?${currentParams.toString()}`, { replace: true });
    }
  }, [key, value, defaultValue, location.search, navigate]);

  // Function to update both state and URL
  const setSyncedValue = useCallback((newValue) => {
    setValue(newValue);
  }, []);

  return [value, setSyncedValue];
}

// How to use it:
// // In your App.js or main router file:
// // import { BrowserRouter as Router } from 'react-router-dom';
// // <Router><MyComponent /></Router>
//
// function FilterableProductList() {
//   const [category, setCategory] = useQueryParamsSync('category', 'all');
//   const [priceMin, setPriceMin] = useQueryParamsSync('minPrice', 0);
//
//   return (
//     <div>
//       <h1>Product List</h1>
//       <p>Current Category: {category}</p>
//       <button onClick={() => setCategory('electronics')}>Electronics</button>
//       <button onClick={() => setCategory('books')}>Books</button>
//       <p>Min Price: ${priceMin}</p>
//       <input
//         type="number"
//         value={priceMin}
//         onChange={(e) => setPriceMin(Number(e.target.value))}
//       />
//     </div>
//   );
// }
How it works: The `useQueryParamsSync` hook allows a React component's state to be synchronized with a specific URL query parameter. It initializes the state from the URL if the parameter exists or uses a `defaultValue`. Any changes to the state trigger a `useEffect` to update the URL's query parameters without a full page reload, ensuring that the component's state is reflected in a shareable and bookmarkable URL. This example assumes `react-router-dom` v6+.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs