如何正确序列化查询参数?

Twi*_*her 5 javascript reactjs react-router react-router-dom

为了检索当前的查询参数,我使用这个:

import { useLocation } from 'react-router-dom';

function useQuery() {
    return new URLSearchParams(useLocation().search);
}
Run Code Online (Sandbox Code Playgroud)

然后在功能组件中:

const query = useQuery();
Run Code Online (Sandbox Code Playgroud)

Link但是,除了具有新值的查询参数之外,我没有找到任何集成的解决方案可以轻松设置为相同的查询参数。

到目前为止,这是我的解决方案:

const filterLink = query => param => value => {
  const clone = new URLSearchParams(query.toString());
  clone.set(param, value);
  return `?${clone.toString()}`;
}

return (
  <>
    <Link to={filterLink(query)('some-filter')('false')}>False</Link>
    <Link to={filterLink(query)('some-filter')('true')}>True</Link>
  </>
)
Run Code Online (Sandbox Code Playgroud)

我必须克隆该query对象,以免改变原始对象,并且filterLink在 JSX 中多次调用时不会产生不需要的副作用。我还必须自己添加问号,因为URLSearchParams.prototype.toString()不添加它。

我想知道为什么我必须自己这样做?我真的不喜欢在使用框架时做如此低级的事情。我错过了反应路由器中的某些东西吗?是否有更常见的做法可以满足我的需要?

Dre*_*ese 6

Angular 是一个框架,而 React 通常被认为仍然是一个库。因此,React 更像是自己动手 (DIY)。React 不关心 URL queryString,react-router主要关心用于路由匹配和渲染的 URL 路径。

但是react-router-dom@6引入了一些新的挂钩和实用函数来帮助处理 queryString 参数。您可能会发现对这项工作有帮助的一个实用程序是createSearchParams函数。

declare function createSearchParams(
  init?: URLSearchParamsInit
): URLSearchParams;
Run Code Online (Sandbox Code Playgroud)

createSearchParams是一个薄包装new URLSearchParams(init),增加了对使用具有数组值的对象的支持。useSearchParams这与内部使用的从值创建URLSearchParams对象的 函数相同URLSearchParamsInit

根据您的问题和此处的其他答案,我假设您不需要简单地删除以前存在的搜索参数并将其替换为当前链接,而是您希望有条件地将新参数与任何现有参数合并。

Link创建一个实用函数,它采用与组件的toprop ( )相同的 props string | Partial<Path>。这是我们关心并想要覆盖的部分类型。Path

import { createSearchParams, To } from "react-router-dom";

interface CreatePath {
  pathname: string;
  search?: {
    [key: string]: string | number;
  };
  hash?: string;
  merge?: boolean;
}

const createPath = ({
  hash,
  merge = true,
  pathname,
  search = {}
}: CreatePath): To => {
  const searchParams = createSearchParams({
    ...(merge
      ? (Object.fromEntries(createSearchParams(window.location.search)) as {})
      : {}),
    ...search
  });

  return { pathname, search: searchParams.toString(), hash };
};
Run Code Online (Sandbox Code Playgroud)

用法:

  1. 将参数与现有 queryString 参数合并的链接:

    <Link to={createPath({ pathname: "/somePage", search: { a: 1 } })}>
      Some Page
    </Link>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 替换现有参数的链接:

    <Link
      to={createPath({ pathname: "/somePage", search: { b: 2 }, merge: false })}
    >
      Some Page
    </Link>
    
    Run Code Online (Sandbox Code Playgroud)

我建议更进一步,创建一个自定义Link组件来为您执行路径创建步骤。

基于上述实用函数的示例:

import { Link as LinkBase, LinkProps as BaseLinkProps } from "react-router-dom";

// Override the to prop
interface LinkProps extends Omit<BaseLinkProps, "to"> {
  to: CreatePath;
}

// Use our new createPath utility
const Link = ({ to, ...props }: LinkProps) => (
  <LinkBase to={createPath(to)} {...props} />
);
Run Code Online (Sandbox Code Playgroud)

用法与上面相同,但现在to直接传递 prop:

  1. 将参数与现有 queryString 参数合并的链接:

    <Link to={{ pathname: "/somePage", search: { a: 1 } }}>
      Some Page
    </Link>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 替换现有参数的链接:

    <Link to={{ pathname: "/somePage", search: { b: 2 }, merge: false }}>
      Some Page
    </Link>
    
    Run Code Online (Sandbox Code Playgroud)

演示:

编辑如何正确序列化查询参数