admin管理员组

文章数量:1134570

In my NextJS app, I have a language selector that's visible on every page. When I select a new language, I just want to replace the current URL by appending a query param lang=en to it.

Here's the function that replaces the URL:

const changeLanguage = (lang: LanguageID) => {
    replace({
      pathname,
      query: { ...query, lang },
    });
  };

In this example, replace, query and pathname are coming from the next router.

Now, everything works for static routes, but I'm unable to make it work for dynamic routes. For example, I have the following folder structure:

pages
|_customers
|__index.tsx
|__[customerId].tsx

If I'm on http://localhost/customers and I change my language to English, the URL changes to http://localhost/customers?lang=en which is what I want. However, if I'm on http://localhost/customer/1 and I change my language to English, the URL changes to http://localhost/customers/[customerId]?customerId=1&lang=en, instead of the URL I'm expecting http://localhost/customers/1?lang=en.

Now, I know that I could use asPath on the router, and reconstruct the query string object by appending lang to it, but I feel that it's something that should be build into Next. Also, I know it could be easily done with vanilla JS, but it's not the point here.

Am I missing something? Is there an easier way to append query params to a dynamic route without doing a server-side re-rendering?

Thanks

In my NextJS app, I have a language selector that's visible on every page. When I select a new language, I just want to replace the current URL by appending a query param lang=en to it.

Here's the function that replaces the URL:

const changeLanguage = (lang: LanguageID) => {
    replace({
      pathname,
      query: { ...query, lang },
    });
  };

In this example, replace, query and pathname are coming from the next router.

Now, everything works for static routes, but I'm unable to make it work for dynamic routes. For example, I have the following folder structure:

pages
|_customers
|__index.tsx
|__[customerId].tsx

If I'm on http://localhost/customers and I change my language to English, the URL changes to http://localhost/customers?lang=en which is what I want. However, if I'm on http://localhost/customer/1 and I change my language to English, the URL changes to http://localhost/customers/[customerId]?customerId=1&lang=en, instead of the URL I'm expecting http://localhost/customers/1?lang=en.

Now, I know that I could use asPath on the router, and reconstruct the query string object by appending lang to it, but I feel that it's something that should be build into Next. Also, I know it could be easily done with vanilla JS, but it's not the point here.

Am I missing something? Is there an easier way to append query params to a dynamic route without doing a server-side re-rendering?

Thanks

Share Improve this question edited Apr 27, 2020 at 23:52 Jean-Francois Gagnon asked Apr 27, 2020 at 19:29 Jean-Francois GagnonJean-Francois Gagnon 3,2804 gold badges21 silver badges27 bronze badges 6
  • Instead of using next.js route api, use window.location. – Jose Felix Commented Apr 27, 2020 at 21:46
  • Question in changeLanguage = (lang: LanguageID) are you trying to set a default value? Or is this TypeScript syntax? – Antonio Pavicevac-Ortiz Commented Apr 27, 2020 at 21:52
  • @JoseFelix like I said in my post, I know it can easily be done in vanilla JS, but I want to avoid that if possible. – Jean-Francois Gagnon Commented Apr 27, 2020 at 21:57
  • I think the easiest way here is to give the pathname property the asPath value from the router. – Jose Felix Commented Apr 27, 2020 at 22:18
  • @JoseFelix the problem with that solution is that it causes a server-side re-rendering, which I want to avoid if possible. That's why I asked the question, but I'm starting to feel like it's not really possible with Next... – Jean-Francois Gagnon Commented Apr 27, 2020 at 23:51
 |  Show 1 more comment

13 Answers 13

Reset to default 72

Just add more param to current router then push itself

const router = useRouter();
router.query.NEWPARAMS = "VALUE"
router.push(router)

The solution which doesn't need to send the whole previous route, as replace just replaces what we need to replace, so query params:

const router = useRouter();
router.replace({
   query: { ...router.query, key: value },
});

If we want to have this as a link - use it like so:

const router = useRouter();


<Link
    href={{
        pathname: router.pathname,
        query: { ...router.query, lang },
    }}
    passHref
    shallow
    replace
></Link>

I tried adding my param to the route query and pushing the router itself, as mentioned here, it works, but I got a lot of warnings:

So, I then pushed to / and passed my query params as following:

const router = useRouter();
router.push({ href: '/', query: { myQueryParam: value  } });

I hope that works for you too.

An alternative approach when you have dynamic routing in Next.js, and want to do a shallow adjustment of the route to reflect updated query params, is to try:

const router = useRouter()
const url = {
      pathname: router.pathname,
      query: { ...router.query, page: 2 }
    }
router.push(url, undefined, { shallow: true })

This will retreive the current path (router.pathname) and query (router.query) details, and merge them in along with your new page query param. If your forget to merge in the existing query params you might see an error like:

The provided href value is missing query values to be interpolated properly

for nextjs 13 I just found this,

import { useRouter } from 'next/navigation';
router.push(`?page=${page}`)

you just need to handle every query you want

I ended up using the solution that I wanted to avoid in the first place, which was to play with the asPath value. Atleast, there's no server-side re-rendering being done since the path is the same.

Here's my updated changeLanguage function (stringifyUrl is coming from the query-string package)

  const changeLanguage = (lang: LanguageID) => {
    const newPathname = stringifyUrl({ url: pathname, query: { ...query, lang } });
    const newAsPath = stringifyUrl({ url: asPath, query: { lang } });
    replace(newPathname, newAsPath);
  };

If anyone is still looking the answer ,for Next,js ^11.1.2.I hope this helps you out.Use

const router = useRouter();
router.push({ pathname: "/search", query: { key: key } });

In latest version, Next 13, some of the functionality moved to other hooks, which query and path are some of them. You can use useSearchParams to get query and usePathname instead of pathname. By the time I am writing this answer, it does not have a stable version and you can find the beta documents here: https://beta.nextjs.org/docs/api-reference/use-router

If you are using App Router, The query object has been removed and is replaced by useSearchParams(), you need to update searchParams:

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()
 
  // Get a new searchParams string by merging the current
  // searchParams with a provided key/value pair
  const createQueryString = useCallback(
    (name: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString())
      params.set(name, value)
 
      return params.toString()
    },
    [searchParams]
  )
 
  return (
    <>
      {/* using useRouter */}
      <button
        onClick={() => {
          // <pathname>?sort=asc
          router.push(pathname + '?' + createQueryString('sort', 'asc'))
        }}
      >
        ASC
      </button>
 
      {/* using <Link> */}
      <Link
        href={
          // <pathname>?sort=desc
          pathname + '?' + createQueryString('sort', 'desc')
        }
      >
        DESC
      </Link>
    </>
  )
} 

The best solution I could come up with is that doesn't cause the Unknown key passed via urlObject into url.format is to do this pattern router.query.plan = plan.title; router.push({ query: router.query });

I see the top answer already resolved this, just wanted to add, if you have for example table filters or page result filters, do not do ‘router.replace()’. Always push to preserve the browser history. This way the user can go back with browser arrows and query params will still work.

  let queryParams;
  if (typeof window !== "undefined") {  
    // The search property returns the querystring part of a URL, including the question mark (?).
    queryParams = new URLSearchParams(window.location.search);
    // quaeryParams object has nice methods
    // console.log("window.location.search", queryParams);
    // console.log("query get", queryParams.get("location"));
  }

inside changeLanguage,

const changeLanguage = (lang: LanguageID) => {
    if (queryParams.has("lang")) {
      queryParams.set("lang", lang);
    } else {
      // otherwise append
      queryParams.append("lang", lang);
    }
    router.replace({
      search: queryParams.toString(),
    });
  };

本文标签: javascriptNextJSAppending a query param to a dynamic routeStack Overflow