Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revalidateTag and revalidatePath redirect to intercepting route when inside dynamic route #59796

Closed
1 task done
kahnclusions opened this issue Dec 20, 2023 · 37 comments · Fixed by #63768
Closed
1 task done
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked

Comments

@kahnclusions
Copy link

kahnclusions commented Dec 20, 2023

Link to the code that reproduces this issue

https://github.com/kahnclusions/nextgram/tree/bug/revalidate-tag-reloads

To Reproduce

  1. Open http://localhost:3000/en in a browser
  2. Click on a photo
  3. Click on "Click me to see the bug" to see the bug

Current vs. Expected behavior

I expected

  • the modal to remain visible
  • the rest of my click handler to execute
  • the component state to update
  • the component to re-render and display the text "The answer is: 42"

Instead

  • After the server action completes, the page immediately reloads on the intercepting route. It never executes the rest of the click handler.

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.0.0: Fri Sep 15 14:43:05 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6020
Binaries:
  Node: 20.8.1
  npm: 10.1.0
  Yarn: N/A
  pnpm: 8.10.5
Relevant Packages:
  next: 14.0.4
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

App Router

Additional context

The change that causes the bug is adding the [locale] directory and putting everything into the dynamic route. This is a necessary step for doing basic internationalisation. The server action with revalidateTag/Path seems to work fine when added to the base NextGram repo, but after adding the [locale]/ directory and moving everything in there causes it to break.

I can reproduce the issue locally in multiple browsers.

The code here never executes when the bug happens: https://github.com/vercel-labs/nextgram/compare/main...kahnclusions:nextgram:bug/revalidate-tag-reloads?expand=1#diff-17ba861615c90b344dff6c88255f992f1cfaae5da0f4d414d9e64d8f518443d8R15-R21

NEXT-2079

@kahnclusions kahnclusions added the bug Issue was opened via the bug report template. label Dec 20, 2023
@kahnclusions
Copy link
Author

Short potato-quality video of the bug:

Screen.Recording.2023-12-20.at.16.31.10.mov

@kahnclusions
Copy link
Author

kahnclusions commented Dec 20, 2023

Even weirder is if I add a redirect("/en") after the revalidate calls, the server action then returns undefined, and no redirect takes place.

@conejocarlo
Copy link

conejocarlo commented Dec 25, 2023

We have the same problem: revalidating any (unrelated) route while on the intercepted route refreshes the route into its full-page version. We are also using next-intl and have all routes inside [locale]/

@max-mayorov
Copy link

We have the same problem - calling revalidateTag in onSubmit of a form breaks intercepting/parallel routes.
Upgrading to 14.0.5-canary.46 has changed the behavior of the app, but it is still broken.
Looking forward to the resolution.

@ztanner ztanner added the linear: next Confirmed issue that is tracked by the Next.js team. label Jan 12, 2024
@decapination-labs
Copy link

is it fixed or not yet?

when i call revalidatePath('/') from server action for opened modal ( intercepting route ) i get redirected to 404

@kahnclusions
Copy link
Author

is it fixed or not yet?

Not as far as I'm aware. I'll update my reproduction to the latest version.

I ended up just removing all usage of intercepting / parallel routes from my project because this feature is so unreliable.

@decapination-labs
Copy link

i guess parallel route is fixed with next update , but not the intercepting route , because when i open the modal and i call a server action then revalidatePath i get 404!

@decapination-labs
Copy link

decapination-labs commented Feb 8, 2024

i guess parallel route is fixed with next update , but not the intercepting route , because when i open the modal and i call a server action then revalidatePath i get 404!

i guess i have different error/behavior then urs .. i have intercepted parallel route .. means i click a preview icon the modal popsup (/explore/preview/product/1) and there is a fullscreen icon that takes me to the route (/explore/product/1)

if inside the modal i call a server action and revalidatePath then i am redirecte to 404 page .. but the action gets executed

Screenshot 2024-02-08 at 5 48 54 PM

@sim391calado
Copy link

I also have this issue. When I intercept a route and the modal page component is shown, if I have a server action in the modal, the background page will refresh, but no 404, it just loses all the background page elements, which is not the ideal situation...

@sim391calado
Copy link

I also have this issue. When I intercept a route and the modal page component is shown, if I have a server action in the modal, the background page will refresh, but no 404, it just loses all the background page elements, which is not the ideal situation...

I just fixed my case by adding the same components into the default.tsx . It's confusing but I got it!

@mstrchng
Copy link

I also have this issue. When I intercept a route and the modal page component is shown, if I have a server action in the modal, the background page will refresh, but no 404, it just loses all the background page elements, which is not the ideal situation...

I just fixed my case by adding the same components into the default.tsx . It's confusing but I got it!

Mind sharing an example of what's going in default.tsx? Thank you!

@kahnclusions
Copy link
Author

I've updated my example reproduction with NextGram to the last version of Next.js, the issue is still present. Calling the revalidate functions from a server action while on an intercepted route reloads the page, seems like it causes the client router cache to be completely wiped.

@sim391calado
Copy link

sim391calado commented Feb 22, 2024

I also have this issue. When I intercept a route and the modal page component is shown, if I have a server action in the modal, the background page will refresh, but no 404, it just loses all the background page elements, which is not the ideal situation...

I just fixed my case by adding the same components into the default.tsx . It's confusing but I got it!

Mind sharing an example of what's going in default.tsx? Thank you!

In essence, my default.tsx will be exactly the same as the page.tsx. Then if I have an intercepting route that opens another page as modal, and I call a server action inside the modal, the background which was page.tsx seems to call the default.tsx, which if you don't have it will give you the 404 error. So you either create an exact copy of page.tsx and call it default.tsx or you could simply create a default.tsx page returning null, but then the page will be blank after you call the server action.

Still, I face another challenge. After calling the server action, I can see that my background page did not change after revalidatingPath("/"), however, if I close the modal to show the background page, it will still refresh the page, probably changing from the default.tsx to the page.tsx ... This behaviour doesn't occur, if I don't call the server action with revalidatePath on the modal.

It's not a big deal, but this behaviour is still not the ideal one...

Edit: Seems that this is because when I close the modal, I'm using router.back() and so it goes from default.tsx to page.tsx for some weird reason. I don't know how can I fix this.

@sim391calado
Copy link

Mind sharing an example of what's going in default.tsx? Thank you!

I've got a new solution, instead of using page.tsx and default.tsx, I've moved my components to layout.tsx, perhaps create a specific folder for your page, so that the layout only afects pages inside that folder.

With this, the revalidatePath works inside the modal and doesn't break anything, also if you close the modal it will go back to the previous page without any refresh. Make sure to use next links with scroll={false} so that it won't scroll to the top when you open a page inside a modal!

@dcapitator
Copy link

I've updated my example reproduction with NextGram to the last version of Next.js, the issue is still present. Calling the revalidate functions from a server action while on an intercepted route reloads the page, seems like it causes the client router cache to be completely wiped.

for me its reloading to 404 page because the intercepted route is nested in a group route i guess

@dcapitator
Copy link

I also have this issue. When I intercept a route and the modal page component is shown, if I have a server action in the modal, the background page will refresh, but no 404, it just loses all the background page elements, which is not the ideal situation...

I just fixed my case by adding the same components into the default.tsx . It's confusing but I got it!

Mind sharing an example of what's going in default.tsx? Thank you!

In essence, my default.tsx will be exactly the same as the page.tsx. Then if I have an intercepting route that opens another page as modal, and I call a server action inside the modal, the background which was page.tsx seems to call the default.tsx, which if you don't have it will give you the 404 error. So you either create an exact copy of page.tsx and call it default.tsx or you could simply create a default.tsx page returning null, but then the page will be blank after you call the server action.

Still, I face another challenge. After calling the server action, I can see that my background page did not change after revalidatingPath("/"), however, if I close the modal to show the background page, it will still refresh the page, probably changing from the default.tsx to the page.tsx ... This behaviour doesn't occur, if I don't call the server action with revalidatePath on the modal.

It's not a big deal, but this behaviour is still not the ideal one...

Edit: Seems that this is because when I close the modal, I'm using router.back() and so it goes from default.tsx to page.tsx for some weird reason. I don't know how can I fix this.

export default function Default() {
  return null;
}

returning null after calling SA inside intercepted route is redirecting me to blank 404 page !!

@dcapitator
Copy link

@samcx can ui please review this issue ?

@sim391calado
Copy link

Anyone found a solution for this?

@JesperCph
Copy link

The problem remains unresolved in the newly released v14.1.2. Due to the prolonged unresolved status of this bug, we have lost confidence that this will get resolved within a foreseeable future.

@steve-marmalade
Copy link

Also interested in this. Am I correct that this is the same core problem as in #51714 (which I believe is the one being tracked, at least based on Zack's comment #60815 (comment))

@sim391calado
Copy link

The problem remains unresolved in the newly released v14.1.2. Due to the prolonged unresolved status of this bug, we have lost confidence that this will get resolved within a foreseeable future.

My temporary solution is to open a modal using searchParams. For example you can use a link to change route to href={"/?type=modal"} and then mount the modal when isPath is true like:
const isPath = searchParams.get("type") === "modal" ? true : false;

This way I'm still able to do the server actions and nothing breaks. Still, I'd love to use the intercepting modals as a hard refresh would lead to the normal route.

@kahnclusions
Copy link
Author

Also interested in this. Am I correct that this is the same core problem as in #51714 (which I believe is the one being tracked, at least based on Zack's comment #60815 (comment))

They all seem related to me... the client side router cache gets blown away when revalidating, and so it forces a reload, which for this particular use case means reloading onto the intercepted route.

My "workaround" is similar to @sim391calado -- just don't use these features yet until they are actually stable. I'm also using a similar approach decide when to show a modal. Definitely lost a lot of confidence in the product because of this, it feels like these features were rushed out without much testing, clearly no one is using them in serious production apps if these basic use cases don't work (submit a pop-up form on an internationalised app).

@samcx
Copy link
Member

samcx commented Mar 8, 2024

@dcapinator I can confirm this is still an issue.

@kahnclusions I understand this pain—our parallel/intercepting routes has received a ton of bug fixes, but there are still bugs such as this one. We are still looking to fix these very soon! 🙇🏼

@titiloxx
Copy link

Hello I really love this feature but I am experiencing this bug too

@Aldo-Garza
Copy link

Also I would love to use this for forms inside modals w/ parallel routes but the bug completely breaks it.

@damian-balas
Copy link

damian-balas commented Mar 19, 2024

What should I do to "refresh" parallel route modal data that was fetched on server? revalidatePath and router.refresh() redirects me to the intercepted page and leaves the parallel route modal open.

In error.tsx reset function also only rerenders the client component, but no refetch happenes.

this is my temporary fix:

          <Button
              onClick={() => {
                // * this is a hacky way I've implemented to handle the refetching of the server component data
                const query = qs.stringify(
                  {
                    ...Object.fromEntries(searchParams),
                    ts: Date.now(),
                  },
                  { addQueryPrefix: true },
                );
                setTimeout(() => {
                  router.push(`${pathname}${query}`, { scroll: false });
                }, 50);
                router.back();
              }}>
              refresh
            </Button>

Only then the parallel route runs the awaited fetch call.

@Aldo-Garza
Copy link

Might be fixed by https://github.com/vercel/next.js/releases/tag/v14.2.0-canary.33, I haven't had the opportunity to test it.

@samcx
Copy link
Member

samcx commented Mar 26, 2024

@Aldo-Garza Let us know if that push fixed the issue!

@roksui
Copy link

roksui commented Mar 27, 2024

@Aldo-Garza @samcx I have confirmed that with #63263, revalidating in a server action called by a client component with useTransition in a parallel/intercepted route(i.e. modal) does not anymore redirect to its corresponding page nor cause any server action error as discussed in observation 2 in #62102.

@BobbiSixkiller
Copy link

I tested it with the latest canary version and the bug persists. If I revalidate tag within a server action on form submission, the intercepting route (dialog) is refreshed and the route that is being intercepted is rendered.

@dcapitator
Copy link

and once rendered is the modal is not in the root folder , instead in a group route then i get 404 error

ztanner added a commit that referenced this issue Mar 28, 2024
)

### What
When calling `revalidatePath` or `revalidateTag` in a server action for
an intercepted route with dynamic segments, the page would do a full
browser refresh.

### Why
When constructing rewrites for interception routes, the route params
leading up to the interception route are "voided" with a
`__NEXT_EMPTY_PARAM__` demarcation. When it comes time to look up the
values for these dynamic segments, since the params aren't going to be
part of the URL, they get matched via `FlightRouterState`
([ref](https://github.com/vercel/next.js/blob/d67d658ce7396c4b6be1b724be5f45763d5a1803/packages/next/src/server/app-render/app-render.tsx#L153-L201)).

The `shouldProvideFlightRouterState` variable only passes it through for
RSC requests; however, since the server action will perform the action &
return the flight data in a single pass, that means the updated tree
from the server action isn't going to receive the `FlightRouterState`
when constructing the new tree. This means the old tree will have a
`["locale", "en", "d"]` segment, and the new tree from the server action
will have `"[locale]"`. When the router detects this kind of segment
mismatch, it assumes the user navigated to a new root layout, and
triggers an MPA navigation.

### How
This unconditionally provides the `FlightRouterState` to
`makeGetDynamicParamFromSegment` so that it can properly extract dynamic
params for interception routes. We currently enforce interception routes
to be dynamic due to this `FlightRouterState` dependency.

Fixes #59796
Closes NEXT-2079
@kahnclusions
Copy link
Author

Ignore my last message (deleted), I misunderstood where the fix was.

The canary versions mentioned above do not fix the issue.

However @samcx I've tested the branch in #63768 locally with my nextgram repro branch, and yes this does seem to resolve the issue! 🙌

@andrej-gajdos-cf
Copy link

@samcx is this fix included in 14.2.0-canary.60?

@Dastari
Copy link

Dastari commented Apr 7, 2024

@samcx is this fix included in 14.2.0-canary.60?

I can confirm you can use revalidatePath in server actions submitted using useFormState in the canary.60 release and it no longer redirects to a 404.

Yay :)

@dcapitator
Copy link

i confirm its fixed

@zhefciad
Copy link

zhefciad commented Apr 8, 2024

I confim it's also fixed, but it doesn't work on prod (npm run start), only in dev (npm run dev). Am I doing something wrong?

I'm getting a 404 error (see image).

WindowsTerminal_IAZACF5xpa

Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. locked
Projects
None yet
Development

Successfully merging a pull request may close this issue.