react-router
react-router copied to clipboard
[Bug]: State is not preserved in a component tree when switching to render via custom `<Routes location={location}>` prop
What version of React Router are you using?
6.26.2
Steps to Reproduce
- Render a
<Routes location={location}>
component that potentially receives a customlocation
prop, initiallynull
. - Perform the action that triggers this
location
prop value to change, changing it to the same current browser location.- This means that the route being rendered should not change. It should still render the same page it was rendering before. It's just that it now renders it from the custom location value, instead of being driven by the browser's URL.
There's a test case provided in the pull request that proposes a fix to this issue (https://github.com/remix-run/react-router/pull/11901). This test case shows in more detail how to reproduce.
This was originally reported in https://github.com/remix-run/react-router/pull/9094#issuecomment-2274059216
Details
To illustrate, check at this short screen recording where I show how the component tree re-rendered in full, because the tree change after having introduced a
Location.Provider
in between the parentRoutes
and the childRenderedRoute
:https://github.com/user-attachments/assets/ce6c7ba1-3959-4b2d-b301-cc9bacfbb2c8
Note how before the rendering commit took place, the tree starts with
Routes
->RenderedRoute
, and after the commit took place, the structure is nowRoutes
->Location.Provider
->RenderedRoute
. This causesRenderedRoute
and everything below it to not merely update, but to be unmounted, and re-created entirely. This in turn causes elements to be re-created, which can cause state in them to be lost.This is understandable due to how React reconciliation works. But it is undesirable, due to the potential to lose inner component state in this entire sub-tree.
Expected Behavior
The tree of components under the rendered route should not be recreated by React. It should be preserved, and, if anything, just updated. But not re-mounted. A clear indication that it is working as expected is if any internal state inside this subtree is preserved, instead of being reset.
Actual Behavior
This tree of components is re-mounted, instead of just updated. Causing any internal state in this subtree to be reset / lost.