GoonGamja
GoonGamja

Reputation: 2286

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?

Upvotes: 11

Views: 10560

Answers (2)

Christian
Christian

Reputation: 124

You are likely seeing re-renders caused by state changes caused by useRouter 's state changing due to the "new" route.

An easy fix for this is to use Router instead of useRouter. Both function the same as useRouter is just a RouterContext to the Class:

useRouter: https://github.com/vercel/next.js/blob/canary/packages/next/client/router.ts#L127-L129

Router: https://github.com/vercel/next.js/blob/canary/packages/next/next-server/lib/router/router.ts#L505

Be aware that you'll need to change the parent components in your component tree to also use Router instead of useRouter.

Upvotes: 0

Nick Rameau
Nick Rameau

Reputation: 1318

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.

Upvotes: 11

Related Questions