-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Description
I'm using React Router as a...
framework
Reproduction
I've created a repo that shows the bug with a very detailed instructions on what's happening and how to reproduce this issue:
https://github.com/amadeus/rr-params-issue
One should be able to easily clone the repo, install deps, run the dev server and then follow along with the console logs and README to get exact details on the issue.
System Info
System:
OS: macOS 15.4.1
CPU: (16) arm64 Apple M4 Max
Memory: 60.70 GB / 128.00 GB
Shell: 4.0.0 - /usr/local/bin/fish
Binaries:
Node: 22.14.0 - ~/.proto/shims/node
npm: 10.9.2 - ~/.local/state/fnm_multishells/89190_1745882005446/bin/npm
pnpm: 10.4.0 - ~/.proto/shims/pnpm
bun: 1.2.2 - ~/.proto/shims/bun
Browsers:
Chrome: 135.0.7049.115
Safari: 18.4
npmPackages:
@react-router/dev: ^7.5.3 => 7.5.3
@react-router/node: ^7.5.3 => 7.5.3
@react-router/serve: ^7.5.3 => 7.5.3
react-router: ^7.5.3 => 7.5.3
vite: ^5.4.18 => 5.4.18Used Package Manager
npm
Expected Behavior
I believe the expectation is that when using a hook like useParams, it should only broadcast context updates when params actually change. I recognize that things like react compiler ultimately help downplay the effects of these updates, but I'd still argue given the surface API of hook, it should only trigger a re-render when params actually change. It's potentially a silent footgun that can be hard to reason about and plan for.
Actual Behavior
The useParams hook appears to trigger extra renders around navigation events with new params that aren't actually new and just the same params from prior to the navigation (I.E. the returned objects from useParams aren't even stable despite being shallowly equal). Then they emit after the url actually changes with the new expected params.
This results in ghost/phantom renders right before navigation events that is often already an expensive sequence of events given page navigation events.
Ultimately I think there are two core issues here:
useParamsshould only fire when params actually changeuseParamsshould always return a stable object for params when they are actually shallowly equal (not really an issue if they only fire appropriately, at least from what I could tell in practice)