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

问题描述 投票:0回答:3

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

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

function useQuery() {
    return new URLSearchParams(useLocation().search);
}

然后在功能组件中:

const query = useQuery();

但是,我没有找到任何集成解决方案可以轻松将

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>
  </>
)

我必须克隆

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

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

javascript reactjs react-router react-router-dom
3个回答
6
投票

Angular 是一个框架,而 React 通常被认为仍然是一个库。因此,React 更像是自己动手 (DIY)。 React 不关心 URL queryString,

react-router
主要关心用于路由匹配和渲染的 URL 路径。

但是

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

declare function createSearchParams(
  init?: URLSearchParamsInit
): URLSearchParams;

createSearchParams
new URLSearchParams(init)
的薄包装,增加了对使用数组对象的支持 价值观。这与
useSearchParams
使用的函数相同 内部用于创建
URLSearchParams
对象
URLSearchParamsInit
值。

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

创建一个实用函数,它采用与

Link
组件的
to
属性 (
string | Partial<Path>
) 相同的属性。这是我们关心并想要覆盖的 partial
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 };
};

用途:

  1. 将参数与现有查询字符串参数合并的链接:

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

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

我建议更进一步,创建一个自定义的

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} />
);

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

to
属性:

  1. 将参数与现有查询字符串参数合并的链接:

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

    <Link to={{ pathname: "/somePage", search: { b: 2 }, merge: false }}>
      Some Page
    </Link>
    

演示:

Edit how-to-properly-serialize-query-params


1
投票

我也不知道

react-router
与查询有什么关系。这是我之前设置它的代码。

const _encode = (v) => v.isWellFormed() 
  ? encodeURIComponent(v) 
  : '';

const queryString = (params) => '?' + Object
  .keys(params)
  .map((key) => (_encode(key) + '=' + _encode(params[key])))
  .join('&')

我的两分钱,路由器实际上并不支持这些参数。也许

useQuery
只是有点方便,但除此之外,他们在设置路线时不使用任何这些。


0
投票

这是我的 oneliner 解决方案

import encodeurl from "encodeurl";

Object.entries(inputObject)
    .map(([key, value]) => `${key}=${encodeurl(value)}`)
    .join("&");
© www.soinside.com 2019 - 2024. All rights reserved.