vercel / next.js

The React Framework
https://nextjs.org
MIT License
127.47k stars 27.04k forks source link

App Dir: Avoid intercepted route for hard navigated page #51648

Open schemburkar opened 1 year ago

schemburkar commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #22 SMP Tue Jan 10 18:39:00 UTC 2023
    Binaries:
      Node: 18.14.2
      npm: 9.5.0
      Yarn: 1.22.19
      pnpm: N/A
    Relevant packages:
      next: 13.4.7-canary.4
      eslint-config-next: 13.4.7
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.0.4

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue or a replay of the bug

https://codesandbox.io/p/sandbox/dazzling-hooks-fz6pjv

To Reproduce

  1. Run the code sandbox
  2. Navigate to the search modal using the Search button
  3. Type a query
  4. Refresh the page, full search page should open
  5. Update the search query using input control
  6. The search modal should also show up, along with the page updated.

The search modal should not show up.

Describe the Bug

  1. The home page contains list of articles.
  2. There is a Search button that opens the intercepted /search route in a Modal
  3. On typing in the input, search is supposed to happen (not implemented), url is updated with query string. e.g. /search?q=food

All Good so far.

  1. Now when a User refreshes the page, the full search page appears. say for url /search?q=food
  2. On the page, if user tries to update the search query, the page is updated as well as opens the intercepted /search route in a Modal

Since the user is already on the full /search page, there should not be any requirement to again open the intercepted route

Expected Behavior

When updating the route with a new query string, the page should get updated and not show the intercepted route modal. As the page is already loaded and should not be again intercepted.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

schemburkar commented 1 year ago

Sample Gif of the repro bug

intercept route bug

louisthomaspro commented 1 year ago

I am facing the same issue. Classic example is the shop product list with /product/[id] that opens a preview modal. When user click on "See full details" button in the modal, we want to show the original route (product page) to the user.

Proposition is to have a property bypassRouteInterception in <Link>. It preloads and redirect to the original product page.

<Link href={`/product/${id}`} bypassRouteInterception>See full details</Link>

The workaround is:

onClick={() => { window.location.reload(); }}
schemburkar commented 1 year ago

@louisthomaspro The reload workaround works for your use case perfectly. For me, I'm already on the search page. To update the search results, the router.replace(newUrl) shows modal for me!

The page should be intercepted when user is on another route. Once they are on the actual page, there should not be requirement to intercept the same route anymore!!!

Korusuke commented 1 year ago

Proposition is to have a property bypassRouteInterception in <Link>. It preloads and redirect to the original product page.

<Link href={`/product/${id}`} bypassRouteInterception>See full details</Link>

Something like this would be really useful. For my use-case I want the modal to have a ...Read More button which would open the full page view

sheldon-welinga commented 1 year ago

Am facing the same issue. Looking forward to a fix for this

lukepearce commented 1 year ago

Any update on this? I'm using query strings for variant selection on a product page. The product page is being shown in a modal using intercepting routes. When the page is visited directly, selecting a variant causes the modal to be displayed.

darwinva97 commented 1 year ago

Proposition is to have a property bypassRouteInterception in <Link>. It preloads and redirect to the original product page.

<Link href={`/product/${id}`} bypassRouteInterception>See full details</Link>

Something like this would be really useful. For my use-case I want the modal to have a ...Read More button which would open the full page view

I like that API. Any update?

AlmedinShala commented 10 months ago

Facing the same issue, any update on this. Preferably search params should not trigger the modal to open.

tomjamesallen commented 10 months ago

We need the same functionality. bypassRouteInterception feels like a clean API to me 👍 Our case is that we're using clerk for auth, and in some cases we want the clerk signin / signup components to appear in a modal, and other times not.

sarowarhosen03 commented 8 months ago

anyone found any soludtion . please let me know

jvzaniolo commented 7 months ago

@sarowarhosen03

anyone found any soludtion . please let me know

You could use a normal Anchor tag:

<a href=href={"/product/" + slug}>View more details</a>
sarowarhosen03 commented 7 months ago

@sarowarhosen03

anyone found any soludtion . please let me know

You could use a normal Anchor tag:

<a href=href={"/product/" + slug}>View more details</a>

what if i need to do it with JavaScript i i can use window.location.replace but the main problem is both are making hard reload and broke the single page application experience

vespertilian commented 7 months ago

@sarowarhosen03

I set it up so the root page triggers the modal. It's not an ideal workaround but at least you don't have the same view rendering on top of itself. I also had to pass in the route to go back to when the modal closes.

"use client";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useEffect } from "react";
export default function SearchStocksPage({
  searchParams: { query },
}: {
  searchParams: { query?: string };
}) {
  const pathname = usePathname();
  const router = useRouter();
  const searchParams = useSearchParams();
  let done = false;
  useEffect(() => {
    if (!done) {
      done = true;
      router.push(`${pathname}?${searchParams.toString()}`);
    }
  }, []);
}
sarowarhosen03 commented 7 months ago

i think i got a solution by making a seperate utility function

"use server";
import { redirect } from "next/navigation";

export default async function redirectHard(uri) {
  redirect(uri);
}

then call it from your client component

ryparker commented 4 months ago

Any workarounds for when url query params are updated (via JS nuqs & non-shallow param replacements)?

Currently the only workaround i've found is to redirect to another route.

  1. So if my modal opens under /searchy (app/@search/(.)searchy/page.tsx)
  2. A refresh will open the full page route of /searchy (app/searchy/page.tsx), which just redirects to /search.
  3. User then lands on the full page route for /search (app/search/page.tsx) and is able to non-shallow update the url query params.
// app/searchy/page.tsx
import { redirect, RedirectType } from 'next/navigation';

/**
 * There is a bug where updating url query params (non-shallow) causes the modal to appear on the full page route.
 * https://github.com/vercel/next.js/issues/51648
 *
 * So we open the modal under /searchy and the full page route for /searchy will soft redirect to /search.
 */
export default function Page(props: any): any {
  console.log('searchy page props', props);
  const params = new URLSearchParams(props.searchParams);
  const newPath = `/search?${params.toString()}`;
  redirect(newPath, RedirectType.replace);
}

Hacky

sarowarhosen03 commented 4 months ago

Any workarounds for when url query params are updated (via JS nuqs & non-shallow param replacements)?

Currently the only workaround i've found is to redirect to another route.

  1. So if my modal opens under /searchy (app/@search/(.)searchy/page.tsx)
  2. A refresh will open the full page route of /searchy (app/searchy/page.tsx), which just redirects to /search.
  3. User then lands on the full page route for /search (app/search/page.tsx) and is able to non-shallow update the url query params.
// app/searchy/page.tsx
import { redirect, RedirectType } from 'next/navigation';

/**
 * There is a bug where updating url query params (non-shallow) causes the modal to appear on the full page route.
 * https://github.com/vercel/next.js/issues/51648
 *
 * So we open the modal under /searchy and the full page route for /searchy will soft redirect to /search.
 */
export default function Page(props: any): any {
  console.log('searchy page props', props);
  const params = new URLSearchParams(props.searchParams);
  const newPath = `/search?${params.toString()}`;
  redirect(newPath, RedirectType.replace);
}

Hacky

Any workarounds for when url query params are updated (via JS nuqs & non-shallow param replacements)?

Currently the only workaround i've found is to redirect to another route.

  1. So if my modal opens under /searchy (app/@search/(.)searchy/page.tsx)
  2. A refresh will open the full page route of /searchy (app/searchy/page.tsx), which just redirects to /search.
  3. User then lands on the full page route for /search (app/search/page.tsx) and is able to non-shallow update the url query params.
// app/searchy/page.tsx
import { redirect, RedirectType } from 'next/navigation';

/**
 * There is a bug where updating url query params (non-shallow) causes the modal to appear on the full page route.
 * https://github.com/vercel/next.js/issues/51648
 *
 * So we open the modal under /searchy and the full page route for /searchy will soft redirect to /search.
 */
export default function Page(props: any): any {
  console.log('searchy page props', props);
  const params = new URLSearchParams(props.searchParams);
  const newPath = `/search?${params.toString()}`;
  redirect(newPath, RedirectType.replace);
}

Hacky

Instead of doing this redirect from server using a server action might be a good option