关于 search-params
面向 React 开发者的 URL 搜索参数状态管理指南,介绍如何为页面内状态与可导航步骤选择浏览器历史行为。内容提供经过验证的单参数 Hook、多参数更新和哈希导航模式。
主要功能
- 01区分页面内状态与导航步骤
- 02使用 Zod 校验单个参数
- 03以 replace 更新多个参数
- 04为哈希导航选择 push 或 replace
- 05保存后规范化 URL
适用场景
- 01实现筛选、排序和分页状态
- 02在 URL 中保存标签页选择
- 03构建支持后退操作的多步向导
- 04保存后更新资源 ID
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ promptfoo/ promptfoo --skill search-params安装后在 Claude Code 中输入/search-params确认已加载。
详细介绍
URL 搜索参数状态管理
决策框架
更新 URL(搜索参数或哈希)时,根据变更代表的是页面内状态还是用户可导航的步骤,在 replace 和 push 之间进行选择:
| 变更类型 | 示例 | 历史记录行为 |
|---|---|---|
| 页面内状态 | 筛选条件、排序、分页、标签页切换、搜索查询 | replace - 不要污染历史记录 |
| 可导航的步骤 | 向导流程、多步骤表单 | push - 返回按钮应回到上一步 |
| 不确定? | 选择前请询问开发者 |
为何重要: 将页面内状态变更推入历史记录会使浏览器历史记录变得杂乱。用户点击“返回”时,预期是离开页面,而不是撤销筛选条件的切换。这是导致“返回按钮失效”问题的首要原因。
正确模式
单个搜索参数 - 使用 useSearchParamState(首选)
此 Hook 使用 Zod 进行验证,并且始终在内部使用 replace: true,因此可以自动获得正确的历史记录行为。
import { useSearchParamState } from '@app/hooks/useSearchParamState';
import { z } from 'zod';
const TabSchema = z.enum(['overview', 'details', 'settings']);
const [activeTab, setActiveTab] = useSearchParamState('tab', TabSchema, 'overview');
关键文件: src/app/src/hooks/useSearchParamState.ts
多个搜索参数 - 使用 setSearchParams 并设置 replace: true
一次更新多个参数时,直接使用 setSearchParams,但对于页面内状态,始终传入 { replace: true }:
const [searchParams, setSearchParams] = useSearchParams();
// Updating filters (in-page state -> replace)
setSearchParams(
(params) => {
params.set('status', 'active');
params.set('sort', 'name');
return params;
},
{ replace: true },
);
基于哈希的导航 - 使用带 replace 或 push 的 navigate()
对于返回按钮应能在各步骤间切换的向导/多步骤流程,使用 push(默认行为):
// Wizard step navigation - push so back button works between steps
// See: src/app/src/pages/redteam/setup/page.tsx
const updateHash = (newStep: string) => {
navigate(`#${newStep}`); // push (default) - intentional
};
对于代表页面内状态的哈希变更,使用 replace:
// Tab switch on a detail page - replace to avoid history clutter
navigate(`#${section}`, { replace: true });
保存后的 URL 规范化
如果需要在创建/保存操作后更新 URL 以包含新的 ID(而非用户操作),请使用 replace:
// After first save, update URL to include new ID without adding history entry
navigate(`/evals/${newConfigId}`, { replace: true });
反模式
推入页面内状态变更(会导致返回按钮失效)
// WRONG - every filter change adds a history entry
setSearchParams((params) => {
params.set('filter', value);
return params;
});
// WRONG - navigate without replace for state change
navigate(`?tab=${newTab}`);
单个参数未验证就直接使用 useSearchParams
// WRONG - no validation, easy to forget { replace: true }
const [searchParams, setSearchParams] = useSearchParams();
const tab = searchParams.get('tab');
const setTab = (v: string) => {
setSearchParams((p) => {
p.set('tab', v);
return p;
});
};
// RIGHT - use the hook instead
const [tab, setTab] = useSearchParamState('tab', TabSchema, 'overview');
使用空字符串而不是 null
// WRONG - useSearchParamState will throw an invariant error
setTab('');
// RIGHT - use null to clear a param
setTab(null);
关键文件
src/app/src/hooks/useSearchParamState.ts- 主要 Hook(内部使用 replace)src/app/src/pages/eval/components/ResultsView.tsx- 正确使用{ replace: true }的示例src/app/src/pages/redteam/setup/page.tsx- 在向导步骤中有意使用 push 的示例
评论
支持 Markdown