React/Next.js数据过滤:保留旧筛选条件并添加新筛选条件
解决Next.js多筛选条件下URL参数被清除的问题
问题现象
实现数据过滤功能时,设置新筛选条件会清除旧参数。比如执行搜索后,URL中的tag等其他筛选参数会被移除,无法实现localhost:3000/?search=text&tag=food这样的多参数共存效果。
现有代码问题分析
原搜索组件中直接使用push("/?search=" + e.target.value),这种方式会完全替换URL的查询参数部分,丢失已有的其他筛选参数(如tag、price)。
搜索组件原代码
import React, { useState } from "react"; import { XMarkIcon } from "@heroicons/react/24/outline"; export default function Search({ useRouter, search }) { const [searchQuery, setSearchQuery] = useState(search); const { push } = useRouter(); const handleInputChange = (e) => { setSearchQuery(e.target.value); push("/?search=" + e.target.value); }; const cleanSearch = (e) => { e.preventDefault(); setSearchQuery(""); push("/?search="); }; return ( // 省略其他渲染代码 <input type="search" id="default-search" className="block w-full rounded-lg border border-slate-300 bg-slate-50 p-4 pl-10 text-sm placeholder-slate-400 focus:border-blue-500 focus:ring-blue-500" placeholder="Search AI tool or category" required value={searchQuery} onChange={handleInputChange} /> // 省略其他渲染代码 ); }
筛选器组件代码
"use client"; import React from "react"; import Search from "../common/search"; import Selector from "../common/selector"; import { useRouter } from "next/navigation"; export default function Filters({ tags, prices, search }) { return ( <div className="mb-5 flex w-full grid-cols-4 flex-col gap-3 text-center text-base font-medium text-slate-700 md:grid"> <Search className="col-span-2 w-full" search={search} useRouter={useRouter} /> <Selector label="Category" data={tags} useRouter={useRouter} /> <Selector label="Price" data={prices} useRouter={useRouter} /> </div> ); }
修正方案
你尝试的updateQueryParams思路是正确的,但需要调整几个细节,确保参数安全更新且符合Next.js的使用规范:
修正后的搜索组件代码
"use client"; import React, { useState } from "react"; import { XMarkIcon } from "@heroicons/react/24/outline"; import { useRouter } from "next/navigation"; export default function Search({ search }) { const [searchQuery, setSearchQuery] = useState(search || ""); const router = useRouter(); const updateQueryParams = (newParams) => { // 基于当前URL的查询参数构建实例 const currentParams = new URLSearchParams(window.location.search); // 遍历新参数,更新或移除对应项 Object.entries(newParams).forEach(([key, value]) => { if (value.trim() === "") { currentParams.delete(key); } else { currentParams.set(key, value.trim()); } }); // 生成新URL并跳转 const newUrl = `${window.location.pathname}${currentParams.toString() ? `?${currentParams.toString()}` : ""}`; router.push(newUrl); }; const handleInputChange = (e) => { const value = e.target.value; setSearchQuery(value); updateQueryParams({ search: value }); }; const cleanSearch = (e) => { e.preventDefault(); setSearchQuery(""); updateQueryParams({ search: "" }); }; return ( // 保留原渲染代码 <input type="search" id="default-search" className="block w-full rounded-lg border border-slate-300 bg-slate-50 p-4 pl-10 text-sm placeholder-slate-400 focus:border-blue-500 focus:ring-blue-500" placeholder="Search AI tool or category" required value={searchQuery} onChange={handleInputChange} /> // 保留原渲染代码 ); }
筛选器组件的调整
无需传递useRouter作为props,组件内部直接调用钩子即可:
"use client"; import React from "react"; import Search from "../common/search"; import Selector from "../common/selector"; export default function Filters({ tags, prices, search }) { return ( <div className="mb-5 flex w-full grid-cols-4 flex-col gap-3 text-center text-base font-medium text-slate-700 md:grid"> <Search className="col-span-2 w-full" search={search} /> <Selector label="Category" data={tags} /> <Selector label="Price" data={prices} /> </div> ); }
Selector组件参考实现(同逻辑处理参数)
Selector组件也需要复用相同的参数更新逻辑,示例如下:
"use client"; import React from "react"; import { useRouter } from "next/navigation"; export default function Selector({ label, data }) { const router = useRouter(); const paramKey = label.toLowerCase(); const currentValue = new URLSearchParams(window.location.search).get(paramKey); const handleChange = (e) => { const value = e.target.value; const currentParams = new URLSearchParams(window.location.search); if (value === "") { currentParams.delete(paramKey); } else { currentParams.set(paramKey, value); } const newUrl = `${window.location.pathname}${currentParams.toString() ? `?${currentParams.toString()}` : ""}`; router.push(newUrl); }; return ( <select onChange={handleChange} value={currentValue || ""} className="your-select-class"> <option value="">All {label}</option> {data.map(item => ( <option key={item.id} value={item.value}>{item.name}</option> ))} </select> ); }
关键要点
- 使用
URLSearchParams安全操作查询参数,避免手动拼接字符串的错误。 - 空值处理:当筛选值为空时,从URL中移除对应参数,而非保留空值。
- 组件内部直接调用
useRouter钩子,符合Next.js的钩子使用规则,无需通过props传递。 - 所有筛选组件统一参数更新逻辑,确保多筛选条件共存。
内容的提问来源于stack exchange,提问作者Wamz
相关产品推荐
相关产品推荐

