You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 02:35:29