mcpField
U
using-nuqs

using-nuqs

github.com/andrelandgraf开发23104 次安装20 周前更新
代码仓库

关于 using-nuqs

面向 React 开发者的 nuqs 使用指南,用于将界面状态同步到 URL 查询参数。内容涵盖 Suspense 边界、解析器、清除状态和可分享的对话框。

主要功能

  1. 01将 React 状态同步到 URL 参数
  2. 02使用解析器和默认值
  3. 03清除单个或多个参数
  4. 04构建可深链访问的对话框
  5. 05就近设置 Suspense 边界

适用场景

  1. 01创建可分享的搜索和筛选链接
  2. 02通过链接打开对话框
  3. 03从 URL 清除筛选项

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/andrelandgraf/fullstackrecipes --skill using-nuqs

安装后在 Claude Code 中输入/using-nuqs确认已加载。

详细介绍

使用 nuqs

使用 nuqs 管理 URL 查询参数中的 React 状态。涵盖 Suspense 边界、解析器、清除状态以及可通过深层链接打开的对话框。

实现使用 nuqs

使用 nuqs 管理 URL 查询参数中的 React 状态,以实现可分享的筛选条件、搜索和可通过深层链接打开的对话框。

参见:


Suspense 边界模式

nuqs 在底层使用 useSearchParams,因此需要 Suspense 边界。通过包装组件使用 Suspense 包裹使用 nuqs 的组件,使边界与组件保持在一起:

import { Suspense } from "react";

type SearchInputProps = {
  placeholder?: string;
};

// Public component with built-in Suspense
export function SearchInput(props: SearchInputProps) {
  return (
    <Suspense fallback={<input placeholder={props.placeholder} disabled />}>
      <SearchInputClient {...props} />
    </Suspense>
  );
}
"use client";

import { useQueryState, parseAsString } from "nuqs";

// Internal client component that uses 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}
    />
  );
}

使用此模式后,使用该组件的组件可以直接使用 SearchInput,无需自行添加 Suspense。

将状态同步到 URL 查询参数

将 useState 替换为 useQueryState,即可将状态同步到 URL:

"use client";

import {
  useQueryState,
  parseAsString,
  parseAsBoolean,
  parseAsArrayOf,
} from "nuqs";

// String state (search, filters)
const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));

// Boolean state (toggles)
const [showArchived, setShowArchived] = useQueryState(
  "archived",
  parseAsBoolean.withDefault(false),
);

// Array state (multi-select)
const [tags, setTags] = useQueryState(
  "tags",
  parseAsArrayOf(parseAsString).withDefault([]),
);
清除状态

设为 null 即可从 URL 中移除:

// Clear single param
setSearch(null);

// Clear all filters
function clearFilters() {
  setSearch(null);
  setTags(null);
  setShowArchived(null);
}

使用 .withDefault() 时,设为 null 会清除 URL 参数,但会返回默认值。

可通过深层链接打开的对话框

使用 URL 参数控制对话框的显示状态,以便分享链接:

import { Suspense } from "react";

type DeleteDialogProps = {
  onDelete: (id: string) => Promise<void>;
};

// Public component with built-in Suspense
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>
        {/* Confirmation UI */}
        <Button onClick={handleDelete}>Delete</Button>
      </AlertDialogContent>
    </AlertDialog>
  );
}

以编程方式打开对话框:

// Open delete dialog for specific item
setDeleteId("item-123");

// Deep link: /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