'How to change route without re-rendering in Next.js?

I created a filter that when I clicked confirmed button changes url.

For example, When I clicked page 1 and place type guesthouse in paris, url looks like this below.

http://localhost3000/findstay?page=1&per=10&place_type=guest_house&city=paris

Now I want to changed the result when url changed. Suppose when I manually changed query params like below, filter should also changed.

http://localhost3000/findstay?page=1&per=10&place_type=hotel&city=newYork

In order to do this. I created a file name findstay in pages folder and created findstay-filter component inside of components folder.

import React from "react";
import FindstayFilter from "@/components/findstay/findstay-filter";

interface IFindstayProps { };

const Findstay: React.FC<IFindstayProps> = () => {
  return <FindstayFilter />;
};

export default Findstay;

All the logic I have explained are written inside of FindstayFilter component.

And this is the code that pushses url when FindstayFilter is rendered.

 useEffect(() => {
    const { city, page, per, placeType } = query;
    const _page = parseInt(page || 1 as any);
    const _city = city || state.cityState; // paris
    const _placeType = placeType || "";

    router.push({
      pathname: `/findstay`,
      query: {
        page: _page,
        per: 10,
        plage_type: _placeType,
        city: _city,
      }
    }, undefined,
      { shallow: true })

    const initialRender = {
      page: _page,
      placeType: _placeType,
      city: _city,
    }
    
    searchAvailableBookingDays(initialRender).then(res => res.json().then(value => setData(value)));
  }, []);

As you can see I used {shallow: true} option and url does not changes only query params are changed. However when I changed one of queries manually (city=paris -> city=newYork), component re-renders and reset query params that I pushes when component renders first time.

Why shallow option is not working?



Solution 1:[1]

The shallow option is working. Its goal is to not let your browser navigate away. But it definitely triggers a state update, which triggers the re-render.

Surely, you've had occasions where you really need it to trigger that state update so you can use updated query param values.

Now, how do you update the URL without triggering a re-render? You use window.history, not Next router.

So, instead of this:

router.push({
      pathname: `/findstay`,
      query: {
        page: _page,
        per: 10,
        plage_type: _placeType,
        city: _city,
      }
    }, undefined,
      { shallow: true })

You would do this:

const newUrl = `/findstay?page=${_page}&per=10&place_type=_placeType&city=_city`

window.history.replaceState({ ...window.history.state, as: newUrl, url: newUrl }, '', newUrl)

It's ugly, but it works as expected. A really nice way to keep your route synced with your state.

I'd recommend you turn it into a utility function.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nick Rameau