Next.js 14 App Router:如何在查询参数中存储多值筛选条件
在Next.js 14 App Router中实现筛选条件同步到查询参数
客户端组件处理交互与参数更新
在客户端组件中,使用next/navigation提供的useSearchParams和useRouter来读取和修改查询参数,实现筛选状态与URL的同步:
import { useSearchParams, useRouter } from 'next/navigation'; export default function ProductFilters() { const searchParams = useSearchParams(); const router = useRouter(); // 从查询参数解析已选中的筛选条件 const selectedBrands = searchParams.get('brands')?.split(',') || []; const selectedCountries = searchParams.get('countries')?.split(',') || []; // 品牌选择切换逻辑 const toggleBrand = (brand) => { const updatedBrands = selectedBrands.includes(brand) ? selectedBrands.filter(b => b !== brand) : [...selectedBrands, brand]; const newParams = new URLSearchParams(searchParams); // 有选中品牌则设置参数,否则删除该参数 updatedBrands.length ? newParams.set('brands', updatedBrands.join(',')) : newParams.delete('brands'); // 更新路由,保留其他查询参数 router.push(`/products?${newParams.toString()}`, { scroll: false }); }; // 国家选择切换逻辑(与品牌逻辑一致) const toggleCountry = (country) => { const updatedCountries = selectedCountries.includes(country) ? selectedCountries.filter(c => c !== country) : [...selectedCountries, country]; const newParams = new URLSearchParams(searchParams); updatedCountries.length ? newParams.set('countries', updatedCountries.join(',')) : newParams.delete('countries'); router.push(`/products?${newParams.toString()}`, { scroll: false }); }; return ( <div className="filters-container"> <div> <h3>品牌</h3> {['brand1', 'brand2', 'brand3'].map(brand => ( <label key={brand} className="filter-item"> <input type="checkbox" checked={selectedBrands.includes(brand)} onChange={() => toggleBrand(brand)} /> {brand} </label> ))} </div> <div> <h3>国家</h3> {['spain', 'france', 'germany'].map(country => ( <label key={country} className="filter-item"> <input type="checkbox" checked={selectedCountries.includes(country)} onChange={() => toggleCountry(country)} /> {country} </label> ))} </div> </div> ); }
服务器组件读取查询参数并获取数据
在服务器组件(如app/products/page.js)中,直接通过searchParams props获取查询参数,解析后用于数据筛选:
// app/products/page.js import ProductFilters from './ProductFilters'; import ProductList from './ProductList'; export default async function ProductsPage({ searchParams }) { // 解析筛选参数,默认空数组 const brands = searchParams.brands?.split(',') || []; const countries = searchParams.countries?.split(',') || []; // 根据筛选条件获取产品数据(替换为实际API请求) const products = await fetchFilteredProducts(brands, countries); return ( <div className="products-page"> <ProductFilters /> <ProductList products={products} /> </div> ); } // 模拟数据请求函数 async function fetchFilteredProducts(brands, countries) { const query = new URLSearchParams(); if (brands.length) query.set('brands', brands.join(',')); if (countries.length) query.set('countries', countries.join(',')); const res = await fetch(`/api/products?${query.toString()}`, { next: { revalidate: 60 } }); if (!res.ok) throw new Error('Failed to fetch products'); return res.json(); }
关键注意事项
- 空参数处理:当没有选中项时,删除对应的查询参数,避免URL出现无效的空参数(如
?brands=)。 - 路由跳转优化:使用
{ scroll: false }参数,避免筛选切换时页面滚动到顶部,提升用户体验。 - 参数解析兼容:处理
searchParams.brands为undefined的情况,避免调用split时出错。
内容的提问来源于stack exchange,提问作者Chicha
相关产品推荐
相关产品推荐

