Query Parameters and Search Strings

Query parameters and search strings are essential tools in modern web applications for managing state in the URL. They allow developers to pass additional information in a URL without modifying the route path itself. In React applications using React Router, query parameters are commonly used for filtering data, searching content, and managing pagination or sort order.

In this post, we will explore how to access, manipulate, and manage query parameters using React Router’s useSearchParams hook. We will also provide practical examples to demonstrate how query parameters can enhance user experience and enable dynamic URL-based navigation.


What Are Query Parameters?

Query parameters are key-value pairs appended to a URL after a ? symbol. They are often used to pass optional information that influences the rendering or behavior of a page. For example:

https://example.com/products?category=books&sort=price

Here, category and sort are query parameters, with values books and price, respectively.

Characteristics of Query Parameters

  1. They appear after the ? symbol in a URL.
  2. Multiple parameters are separated by &.
  3. They are optional and do not affect the main route path.
  4. Useful for filtering, searching, and pagination.

Accessing Query Parameters Using useSearchParams

React Router provides the useSearchParams hook for managing query parameters. This hook returns a URLSearchParams object that allows you to read and update search parameters easily.

Basic Usage

import React from 'react';
import { useSearchParams } from 'react-router-dom';

const ProductList = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  const category = searchParams.get('category');
  const sort = searchParams.get('sort');

  return (
<div>
  <h2>Products</h2>
  <p>Category: {category}</p>
  <p>Sort By: {sort}</p>
</div>
); }; export default ProductList;

Explanation:

  • useSearchParams() returns an array with the current searchParams and a function setSearchParams to update them.
  • searchParams.get('key') retrieves the value of a query parameter.
  • The component re-renders whenever search parameters change.

Filtering Content Based on Query Parameters

One common use case of query parameters is filtering a list of items. For example, a product page can filter products by category or price range using query parameters.

Example: Product Filtering

import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';

const productsData = [
  { id: 1, name: 'Book A', category: 'books', price: 10 },
  { id: 2, name: 'Book B', category: 'books', price: 20 },
  { id: 3, name: 'Game A', category: 'games', price: 30 },
  { id: 4, name: 'Game B', category: 'games', price: 40 },
];

const ProductList = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const [filteredProducts, setFilteredProducts] = useState(productsData);

  useEffect(() => {
const category = searchParams.get('category');
const sort = searchParams.get('sort');
let filtered = [...productsData];
if (category) {
  filtered = filtered.filter((product) => product.category === category);
}
if (sort === 'price-asc') {
  filtered.sort((a, b) => a.price - b.price);
} else if (sort === 'price-desc') {
  filtered.sort((a, b) => b.price - a.price);
}
setFilteredProducts(filtered);
}, [searchParams]); const handleCategoryChange = (category) => {
setSearchParams({ category });
}; const handleSortChange = (sort) => {
const currentCategory = searchParams.get('category') || '';
setSearchParams({ category: currentCategory, sort });
}; return (
<div>
  <h2>Products</h2>
  <div>
    <button onClick={() => handleCategoryChange('books')}>Books</button>
    <button onClick={() => handleCategoryChange('games')}>Games</button>
  </div>
  <div>
    <button onClick={() => handleSortChange('price-asc')}>Sort by Price Asc</button>
    <button onClick={() => handleSortChange('price-desc')}>Sort by Price Desc</button>
  </div>
  <ul>
    {filteredProducts.map((product) => (
      <li key={product.id}>
        {product.name} - ${product.price}
      </li>
    ))}
  </ul>
</div>
); }; export default ProductList;

Explanation:

  • The useEffect hook updates the product list whenever query parameters change.
  • handleCategoryChange and handleSortChange update the query parameters programmatically.
  • Sorting and filtering are based on the query parameters in the URL.

Updating Query Parameters Programmatically

useSearchParams not only reads query parameters but also allows you to update them dynamically. This is useful for making the application state shareable via the URL.

Example: Updating Search Parameters

const handleSearch = (term) => {
  setSearchParams({ search: term });
};
  • Calling setSearchParams({ search: 'laptop' }) updates the URL to include ?search=laptop.
  • Components using searchParams.get('search') will automatically re-render with the new value.

Handling Multiple Query Parameters

Sometimes, you need to handle multiple parameters simultaneously. useSearchParams makes this straightforward.

Example: Multiple Filters

setSearchParams({ category: 'books', sort: 'price-asc', page: 2 });
  • This updates the URL to: /products?category=books&sort=price-asc&page=2.
  • You can read each parameter using searchParams.get('key').

Example: Search and Filter Page

Here’s a full example combining search, filter, and sort functionality using query parameters:

import React, { useState, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';

const products = [
  { id: 1, name: 'Book A', category: 'books', price: 15 },
  { id: 2, name: 'Book B', category: 'books', price: 25 },
  { id: 3, name: 'Game A', category: 'games', price: 35 },
  { id: 4, name: 'Game B', category: 'games', price: 45 },
];

const ProductSearchPage = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const [displayProducts, setDisplayProducts] = useState(products);

  useEffect(() => {
let filtered = [...products];
const category = searchParams.get('category');
const search = searchParams.get('search');
const sort = searchParams.get('sort');
if (category) {
  filtered = filtered.filter((p) => p.category === category);
}
if (search) {
  filtered = filtered.filter((p) =>
    p.name.toLowerCase().includes(search.toLowerCase())
  );
}
if (sort === 'price-asc') {
  filtered.sort((a, b) => a.price - b.price);
} else if (sort === 'price-desc') {
  filtered.sort((a, b) => b.price - a.price);
}
setDisplayProducts(filtered);
}, [searchParams]); const updateQuery = (key, value) => {
const params = Object.fromEntries([...searchParams]);
params[key] = value;
setSearchParams(params);
}; return (
<div>
  <h1>Product Search</h1>
  <input
    type="text"
    placeholder="Search products"
    value={searchParams.get('search') || ''}
    onChange={(e) => updateQuery('search', e.target.value)}
  />
  <select
    value={searchParams.get('category') || ''}
    onChange={(e) => updateQuery('category', e.target.value)}
  >
    <option value="">All</option>
    <option value="books">Books</option>
    <option value="games">Games</option>
  </select>
  <select
    value={searchParams.get('sort') || ''}
    onChange={(e) => updateQuery('sort', e.target.value)}
  >
    <option value="">Sort By</option>
    <option value="price-asc">Price Ascending</option>
    <option value="price-desc">Price Descending</option>
  </select>
  <ul>
    {displayProducts.map((p) => (
      <li key={p.id}>
        {p.name} - ${p.price}
      </li>
    ))}
  </ul>
</div>
); }; export default ProductSearchPage;

Explanation:

  • The page allows users to search by name, filter by category, and sort by price.
  • Each action updates the query parameters, keeping the URL in sync with the UI.
  • Using Object.fromEntries([...searchParams]) preserves other query parameters while updating one.

Best Practices for Query Parameters

  1. Keep URLs Shareable: Query parameters should reflect the current state so users can bookmark or share URLs.
  2. Use Meaningful Keys: Choose descriptive keys like category, sort, or search.
  3. Validate Inputs: Always validate query parameters to avoid errors.
  4. Preserve Existing Parameters: When updating one parameter, preserve others to maintain state consistency.
  5. Avoid Sensitive Data: Never store passwords or private information in query parameters.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *