react-router icon indicating copy to clipboard operation
react-router copied to clipboard

Multiple consecutive redirects not handled consistently between navigations and fetchers

Open rossipedia opened this issue 1 month ago • 2 comments

Reproduction

Open the reproduction repository in Stackblitz:

https://stackblitz.com/~/github.com/rossipedia/rr-fetcher-redirect-revalidate

  1. Click on "Go to form" link
  2. Click on "Submit with Form"
  3. Observe a new ID is appended to the "ID History" as a result of the loader revalidation
  4. Click on "Go to form" again
  5. Click on "Submit with Fetcher"

System Info

Need to install the following packages:
[email protected]
Ok to proceed? (y) 

  System:
    OS: macOS 26.0.1
    CPU: (16) arm64 Apple M4 Max
    Memory: 4.51 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.7.0 - /Users/bryanross/.local/state/fnm_multishells/98221_1760467039698/bin/node
    Yarn: 1.22.22 - /Users/bryanross/.local/state/fnm_multishells/98221_1760467039698/bin/yarn
    npm: 11.5.1 - /Users/bryanross/.local/state/fnm_multishells/98221_1760467039698/bin/npm
    pnpm: 10.18.1 - /Users/bryanross/.local/state/fnm_multishells/98221_1760467039698/bin/pnpm
    bun: 1.3.0 - /opt/homebrew/bin/bun
    Deno: 2.5.4 - /opt/homebrew/bin/deno
  Browsers:
    Chrome: 141.0.7390.77
    Firefox: 143.0.3
    Safari: 26.0.1
  npmPackages:
    @react-router/dev: ^7.9.4 => 7.9.4 
    @react-router/fs-routes: ^7.9.4 => 7.9.4 
    @react-router/node: ^7.9.4 => 7.9.4 
    @react-router/serve: ^7.9.4 => 7.9.4 
    react-router: ^7.9.4 => 7.9.4 
    vite: ^7.1.10 => 7.1.10

Used Package Manager

npm

Expected Behavior

A new ID should be appended after clicking on "Submit with Fetcher", as the final "settled" route should revalidate.

Actual Behavior

The final route's loader is never revalidated, and so no new ID is appended to the list.

From what I can tell, shouldRevalidate receives a different method when using a Fetcher, than when using a <Form>, which causes shouldRevalidate to return false instead of defaultShouldRevalidate.

Additional details are in the README for the source repository.

rossipedia avatar Oct 15 '25 00:10 rossipedia

The gist of the issue here is that when you submit with a Form, state.navigation gets the submission info (because it's an actual navigation) and when a second redirect happens we are able to proxy the state.navigation submission through because it's still sort of the same navigation.

With fetcher.Form, we send the fetcher submission through to shouldRevalidate for the first navigation from the action redirect, but then after that it's just sort of a normal navigational loader redirect. There's no state.navigation submission info available to proxy along to shouldRevalidate on the second redirect.

Maybe instead of trying to proxy submission info through to subsequent shouldRevalidate callss after redirects, we should just track loaders that have not yet actually been able to revalidate. Internally, we have a concept of when we need to even call shouldRevalidate -- if we know we need to call the loader, then we don't bother calling shouldRevalidate. This is the case for new loaders, or in clientLoader.hydrate scenarios etc.

It seems like maybe this should be extended for redirect scenarios. If we called shouldRevalidate once and decided we need to call a given loader, and that set of loaders redirects to a new location inclusive of the same loader, we should just automatically call it the second time - because it never successfully revalidated on the prior load.

brophdawg11 avatar Oct 16 '25 20:10 brophdawg11

If we called shouldRevalidate once and decided we need to call a given loader, and that set of loaders redirects to a new location inclusive of the same loader, we should just automatically call it the second time - because it never successfully revalidated on the prior load.

This feels like the right (ie: expected) behavior 👍

rossipedia avatar Oct 17 '25 16:10 rossipedia