react-router
react-router copied to clipboard
Multiple consecutive redirects not handled consistently between navigations and fetchers
Reproduction
Open the reproduction repository in Stackblitz:
https://stackblitz.com/~/github.com/rossipedia/rr-fetcher-redirect-revalidate
- Click on "Go to form" link
- Click on "Submit with Form"
- Observe a new ID is appended to the "ID History" as a result of the loader revalidation
- Click on "Go to form" again
- 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.
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.
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 👍