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
- They appear after the
?symbol in a URL. - Multiple parameters are separated by
&. - They are optional and do not affect the main route path.
- 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 currentsearchParamsand a functionsetSearchParamsto 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
useEffecthook updates the product list whenever query parameters change. handleCategoryChangeandhandleSortChangeupdate 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
- Keep URLs Shareable: Query parameters should reflect the current state so users can bookmark or share URLs.
- Use Meaningful Keys: Choose descriptive keys like
category,sort, orsearch. - Validate Inputs: Always validate query parameters to avoid errors.
- Preserve Existing Parameters: When updating one parameter, preserve others to maintain state consistency.
- Avoid Sensitive Data: Never store passwords or private information in query parameters.
Leave a Reply