关于 url-state-patterns
面向 React 开发者的 nuqs 技能,用于将应用状态同步到 URL 查询参数。内容涵盖解析器、Suspense 包装、清除参数及可通过链接访问的对话框。
主要功能
- 01将状态同步到查询参数
- 02使用类型化查询解析器
- 03用 Suspense 包装组件
- 04以 null 清除参数
- 05在 URL 中保存对话框状态
适用场景
- 01让搜索内容随 URL 分享
- 02用解析后的查询值表示筛选条件
- 03通过深层链接打开指定条目的对话框
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ andrelandgraf/ fullstackrecipes --skill url-state-patterns安装后在 Claude Code 中输入/url-state-patterns确认已加载。
详细介绍
URL 状态模式
使用 nuqs 将 React 状态同步到 URL 查询参数。
前置条件
请先完成以下设置指南:
- 使用 nuqs 管理 URL 状态
Suspense 包装器
nuqs 会读取 useSearchParams,因此需要 Suspense 边界。通过导出一个会挂起内部客户端组件的公共包装器,将 Suspense 边界放在组件附近,这样使用者就无需自行添加 Suspense。
import { Suspense } from "react";
type SearchInputProps = { placeholder?: string };
export function SearchInput(props: SearchInputProps) {
return (
<Suspense fallback={<input placeholder={props.placeholder} disabled />}>
<SearchInputClient {...props} />
</Suspense>
);
}
"use client";
import { useQueryState, parseAsString } from "nuqs";
function SearchInputClient({ placeholder = "Search..." }: SearchInputProps) {
const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));
return (
<input
value={search}
onChange={(e) => setSearch(e.target.value || null)}
placeholder={placeholder}
/>
);
}
解析器
使用解析器和 useQueryState 替换 useState。使用 .withDefault() 读取回退值,同时保持 URL 简洁。
"use client";
import {
useQueryState,
parseAsString,
parseAsBoolean,
parseAsArrayOf,
} from "nuqs";
const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));
const [showArchived, setShowArchived] = useQueryState(
"archived",
parseAsBoolean.withDefault(false),
);
const [tags, setTags] = useQueryState(
"tags",
parseAsArrayOf(parseAsString).withDefault([]),
);
清除
将值设为 null,即可从 URL 中移除该值。使用 .withDefault() 时,参数会被清除,但读取结果仍为默认值。
setSearch(null);
function clearFilters() {
setSearch(null);
setTags(null);
setShowArchived(null);
}
可通过深层链接访问的对话框
通过 URL 参数控制对话框的显示状态,使其可分享并在前进/后退导航时保留状态。使用相同的 Suspense 模式进行包装。
import { Suspense } from "react";
type DeleteDialogProps = { onDelete: (id: string) => Promise<void> };
export function DeleteDialog(props: DeleteDialogProps) {
return (
<Suspense fallback={null}>
<DeleteDialogClient {...props} />
</Suspense>
);
}
"use client";
import { useQueryState, parseAsString } from "nuqs";
import { AlertDialog, AlertDialogContent } from "@/components/ui/alert-dialog";
function DeleteDialogClient({ onDelete }: DeleteDialogProps) {
const [deleteId, setDeleteId] = useQueryState("delete", parseAsString);
async function handleDelete() {
if (!deleteId) return;
await onDelete(deleteId);
setDeleteId(null);
}
return (
<AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
<AlertDialogContent>
<Button onClick={handleDelete}>Delete</Button>
</AlertDialogContent>
</AlertDialog>
);
}
只需设置参数,即可从任意位置打开它——setDeleteId("item-123") 会生成深层链接 /items?delete=item-123。
function ItemRow({ item }: { item: Item }) {
const [, setDeleteId] = useQueryState("delete", parseAsString);
return (
<Button variant="ghost" onClick={() => setDeleteId(item.id)}>
Delete
</Button>
);
}
参考资料
评论
支持 Markdown