最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to properly serialize query params? - Stack Overflow

programmeradmin1浏览0评论

In order to retrieve the current query params, I'm using this:

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

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

Then in the functional ponent:

const query = useQuery();

However, I didn't find any integrated solution to easily set Link to the same query params except one with a new value.

Here is my solution so far:

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

I have to clone the query object in order not to mutate the original one and have unwanted side effects when calling multiple times filterLink in the JSX. I also have to add the question mark myself because URLSearchParams.prototype.toString() does not add it.

I am wondering why I have to do that myself? I don't really like to do so low-level things when using a framework. Did I miss something in react-router? Is there a more mon practice to do what I need?

In order to retrieve the current query params, I'm using this:

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

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

Then in the functional ponent:

const query = useQuery();

However, I didn't find any integrated solution to easily set Link to the same query params except one with a new value.

Here is my solution so far:

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

I have to clone the query object in order not to mutate the original one and have unwanted side effects when calling multiple times filterLink in the JSX. I also have to add the question mark myself because URLSearchParams.prototype.toString() does not add it.

I am wondering why I have to do that myself? I don't really like to do so low-level things when using a framework. Did I miss something in react-router? Is there a more mon practice to do what I need?

Share Improve this question edited Jan 25, 2023 at 0:41 Drew Reese 204k18 gold badges244 silver badges273 bronze badges asked May 16, 2021 at 21:23 Guerric PGuerric P 31.9k6 gold badges58 silver badges106 bronze badges 3
  • What sort of "up-to-date answer" are you looking for? Are you still using react-router@5 or are you looking for something that now uses react-router@6? Is the goal here to still use a Link ponent that "injects" specific queryString params into the link target path? Do you need to preserve existing query params? Do you want/need the queryString visible as part of the link's accessibility text? – Drew Reese Commented Jan 19, 2023 at 17:20
  • I'd like a general answer about the current practices in React. As an Angular developer, I'm used to the Angular HTTP client library that provides a convenient way to handle query params while being integrated to the framework, so I'm wondering what's the equivalent in React – Guerric P Commented Jan 19, 2023 at 19:04
  • I guess my question is directed more to understanding the exact behavior you are wanting/expecting. Angular is a framework while React is considered more a library. As such, React is a little more DYI. React doesn't care about the URL queryString, and react-router is mainly interested in the URL path for route matching and rendering. – Drew Reese Commented Jan 20, 2023 at 6:24
Add a ment  | 

3 Answers 3

Reset to default 6 +100

Angular is a framework while React is generally considered to still be a library. As such, React is a little more Do It Yourself (DIY). React doesn't care about the URL queryString, and react-router is mainly interested in the URL path for route matching and rendering.

However, react-router-dom@6 introduced a few new hooks and utility functions to help work with the queryString parameters. One utility that you may find helpful in this endeavor is the createSearchParams function.

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

createSearchParams is a thin wrapper around new URLSearchParams(init) that adds support for using objects with array values. This is the same function that useSearchParams uses internally for creating URLSearchParams objects from URLSearchParamsInit values.

Based on your question and the other answers here I'm assuming you don't simply need to blow away previously existing search params and replace them with the current link, but rather that you want to possibly conditionally merge new parameters with any existing params.

Create a utility function that takes the same props as the Link ponent's to prop (string | Partial<Path>). It's the partial Path types we care about and want to override.

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

Usage:

  1. Link that merges params with existing queryString params:

    <Link to={createPath({ pathname: "/somePage", search: { a: 1 } })}>
      Some Page
    </Link>
    
  2. Link that replaces existing params:

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

I'd advise to take this a step further and create a custom Link ponent that does path creation step for you.

Example building on the above utility function:

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

Usage is same as above but now the to prop is directly passed:

  1. Link that merges params with existing queryString params:

    <Link to={{ pathname: "/somePage", search: { a: 1 } }}>
      Some Page
    </Link>
    
  2. Link that replaces existing params:

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

Demo:

I didn't know react-router has anything to do with query either. This is the code that i used before to set it.

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

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

my two cents, router doesn't actually support these parameters. Maybe useQuery is just a bit handy, but other than that they don't use any of that when setting up the routes.

This is my oneliner solution

import encodeurl from "encodeurl";

Object.entries(inputObject)
    .map(([key, value]) => `${key}=${encodeurl(value)}`)
    .join("&");
发布评论

评论列表(0)

  1. 暂无评论