T
TanStackβ€’12mo ago
optimistic-gold

Is there a way to force throw away the previously rendered instance of the same page?

When navigating between two instances of the same page, i.e. just switching out a param, all the state of the page is maintained, which leads to bugs in our case. Is there a way to force the router to throw away the state of the old page? Like when you add a key to component
50 Replies
genetic-orange
genetic-orangeβ€’12mo ago
you mean router.invalidate() ?
foreign-sapphire
foreign-sapphireβ€’12mo ago
this question needs more detail which kind of params? path? search? using loader()?
optimistic-gold
optimistic-goldOPβ€’12mo ago
Sorry, yeah path params. So we have the route /item/:id. On /item/1 there is a link to /item/2. In the page component there is a useState hook that holds some values. When clicking the link from to /item/2 I need the values in the useState hook to reset to their defaults. I can of course manually reset those, but that's cumbersome and error prone. I'm wondering if there's any built in mechanisms in the router that allows wiping all local state from a page component, instead of keeping it between navigations.
foreign-sapphire
foreign-sapphireβ€’12mo ago
minimal example would be good
optimistic-gold
optimistic-goldOPβ€’12mo ago
Jakob Norlin
StackBlitz
Router Basic File Based Example (forked) - StackBlitz
Run official live example code for Router Basic File Based, created by Tanstack on StackBlitz
optimistic-gold
optimistic-goldOPβ€’12mo ago
Navigate between the two posts. I'd like the thumb to reset between navigations, since it's local state to that page
foreign-sapphire
foreign-sapphireβ€’12mo ago
I see you want what most dont want πŸ˜› namely remounting
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yes, exactly ^^ Is it doable? 😬
foreign-sapphire
foreign-sapphireβ€’12mo ago
everything is
optimistic-gold
optimistic-goldOPβ€’12mo ago
Ofc πŸ˜… But is there a builtin mechanism to get this behaviour atm?
foreign-sapphire
foreign-sapphireβ€’12mo ago
foreign-sapphire
foreign-sapphireβ€’12mo ago
function Wrapper() {
const params = Route.useParams();

return <PostComponent key={params.postId} />;
}
function Wrapper() {
const params = Route.useParams();

return <PostComponent key={params.postId} />;
}
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yeah I guess that will do the trick! But come to think of it, is keeping state really the most common use case when navigating between instances of the same page? I find it very confusing, as it's the opposite of my mental model that my useStates are local to the current instance of the page
foreign-sapphire
foreign-sapphireβ€’12mo ago
it really depends
optimistic-gold
optimistic-goldOPβ€’12mo ago
I would expect the same result if I navigate /posts/1 => / => /posts/2 as if I navigate /posts/1 => /posts/2, but with these defaults the behaviours are different, which probably comes as a surprise to most
foreign-sapphire
foreign-sapphireβ€’12mo ago
we could make it configurable in analogy to loaderDeps. when loaderDeps change, then remount the route component you are the first to bring this up, so not sure how common this is in fact we had the remounting behavior for a short period and we got feedback that people did not want it
optimistic-gold
optimistic-goldOPβ€’12mo ago
Huh okay interesting I wonder what use cases they had, because I struggle to come up with them myself
foreign-sapphire
foreign-sapphireβ€’12mo ago
so would a remountOnDepsChange do the trick for you ? (with a default on router)?
optimistic-gold
optimistic-goldOPβ€’12mo ago
But yeah if I could make a wish it would be a config on the router for a default of defaultRemount: true/false, and then make it overridable on a route level. Or maybe even on a <Link>/navigate() level What's included in depsChange here? I wouldn't want to remount for a search param for example But for a route param, yeah
foreign-sapphire
foreign-sapphireβ€’12mo ago
well then don't include them in the loaderDeps 😁 deps by default are only path params and you can opt into selected search params won't be possible on the link level
optimistic-gold
optimistic-goldOPβ€’12mo ago
But the only way to pass search params into loader is via loaderDeps, right?
foreign-sapphire
foreign-sapphireβ€’12mo ago
yes why "but"?
optimistic-gold
optimistic-goldOPβ€’12mo ago
Because we do change loader data based on search params, and we want that to avoid remounts (I think I start to see the use cases people had with not wanting remounts). But a path param change would be considered a completely new page in our case
foreign-sapphire
foreign-sapphireβ€’12mo ago
so you would need a separate way of describing your remount dependencies then
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yeah I think so My mental model is this: Search params modify state on the current page (which can trigger different filtering of data etc, so usually needs to be a loaderDep), and path params take you to different instances of pages, so should always force remount. That's the behaviour I'd like to be able to configure
genetic-orange
genetic-orangeβ€’12mo ago
(would it be possible to make remountOnDepsChange a selector function ?)
optimistic-gold
optimistic-goldOPβ€’12mo ago
Preferrably on a global level, because I'd want that for all pages
foreign-sapphire
foreign-sapphireβ€’12mo ago
as I wrote above, this really depends on the individual app and interpretation
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yeah ofc I think it's a common way of treating routes, but there are for sure those who want to do it differently
foreign-sapphire
foreign-sapphireβ€’12mo ago
getting that general purpose API right is always a challenge
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yeah for sure! One of the trickiest parts about building a router I assume πŸ˜„
foreign-sapphire
foreign-sapphireβ€’12mo ago
i have some ideas, need to experiment a bit can you please create a github issue referencing this thread and some description so we can properly track this?
optimistic-gold
optimistic-goldOPβ€’12mo ago
Will do! The template picker directs me to discussions for feature requests. Still want it as an issue?
foreign-sapphire
foreign-sapphireβ€’12mo ago
discussions are ... crowded i don't like the interface
optimistic-gold
optimistic-goldOPβ€’12mo ago
Haha ok I'll make it an issue
foreign-sapphire
foreign-sapphireβ€’12mo ago
so an issue would be preferable how would that look like? for individual routes I can imagine an API similar to loaderDeps where you select the path params / search params that should trigger a remount but on global level?
createRouter({
routeTree,
defaultRemountDeps: ({search, params}) => params,
})
createRouter({
routeTree,
defaultRemountDeps: ({search, params}) => params,
})
like this? search and params being typed as the union of all values?
optimistic-gold
optimistic-goldOPβ€’12mo ago
Hmm yeah that might work I have a hard time seeing any other way that would be configured though It makes sense to make it configurable, but also is there really that many ways to configure it? Here's the issue btw https://github.com/TanStack/router/issues/2990
foreign-sapphire
foreign-sapphireβ€’12mo ago
maybe someone wants to remount only when a certain path param changes? i don't know πŸ˜„ so making it configurable like this opens up all possibilities
optimistic-gold
optimistic-goldOPβ€’12mo ago
Yeah I won't argue against that Something irks me about having to add that line to every app I create to get the behaviour that I think should probably be the default. It feels like I'm adding "exotic behaviour". But I understand my perspective is not everyones perspective, and it would also be a big and hard to track breaking change to alter the defaults.
foreign-sapphire
foreign-sapphireβ€’12mo ago
what should be a default needs to be discussed separately it's also a matter of whether we consider this to be a breaking change or a bugfix maybe the default can only change in a 2.0 btw how does react router handle this?
optimistic-gold
optimistic-goldOPβ€’12mo ago
No clue tbh. Haven't used React Router in a long time
foreign-sapphire
foreign-sapphireβ€’12mo ago
Stack Overflow
React router 6 never unmount component when URL parameter changes
We have a problem with the react router v6. When the URL parameters change, it is already using the mount component. Never unmount and mount the component. Produced code example - if switching betw...
foreign-sapphire
foreign-sapphireβ€’12mo ago
some more questions. if you set the default as sketches above, it would apply to all routes. this would mean that upon path param change, ALL routes remount. so also all parent layouts unless we really only pass in path params that this route defines and should a parent path param change also remount child routes? a different approach would be to allow setting a RouteWrap component that router would render around the route component. then you can give it a component that sets a key based on path params this would be much simpler to implement in router
optimistic-gold
optimistic-goldOPβ€’12mo ago
this would mean that upon path param change, ALL routes remount
This would be bad. I only expect a layout to remount if the param that changes is part of that layout's route.
should a parent path param change also remount child routes
Yeah I would expect it to
a different approach would be to allow setting a RouteWrap component that router would render around the route component. then you can give it a component that sets a key based on path params this would be much simpler to implement in router
Yeah, but then it's really not very far from the initial solution you posted with creating a wrapper manually. Not sure adding that to the router adds much value
foreign-sapphire
foreign-sapphireβ€’12mo ago
I thought some more about why your remount requirement is rare I often have routes that don't have local state they only use search/path params so why remount / re-render them upon param change? so if this is ever implemented, it probably won't be the default a layout/pathless route cannot define a path param, so this would only apply for search params then?
optimistic-gold
optimistic-goldOPβ€’12mo ago
so why remount / re-render them upon param change?
But as soon as you do use state, this would become a problem. So it's like a ticking time bomb bug as long as not remounting is the default. Everyone who goes from not having state to having state will be in for a surprise. With remounting as default it would be a slight deopt for those who don't have state, but without remounting as the default the outcome is buggy UIs for everyone who adds state to a page. Shouldn't correctness be considered before perf here?
foreign-sapphire
foreign-sapphireβ€’12mo ago
I wouldnt go as far and say this is a matter of "correctness"...
optimistic-gold
optimistic-goldOPβ€’12mo ago
I guess that's on a case by case basis, but in every place in our app where we use state, it does indeed cause incorrect behaviour if it is not isolated to the current path (path as in pathname excluding search params) Sorry I don't think I meant define a path param. Just being part of it. Here's an example. If we have the route /$account/_pathless-layout/$category/posts/$postId, then * If $postId changes, only the page defined at the full route gets remounted. * If $account changes, every single layout gets remounted * If $category changes, the /$account and /$account/_pathless-layout layouts are not remounted, but $category and everything below it in the hierarchy is * If a search param changes, nothing is remounted I checked how Next.js does it, and it behaves the way I would expect here (at the page level, haven't checked all layout behaviours) https://stackblitz.com/edit/nextjs-snnmxbxg?file=app%2F%5BpostId%5D%2Fpage.tsx
foreign-sapphire
foreign-sapphireβ€’12mo ago
so it's 2:1 😁
optimistic-gold
optimistic-goldOPβ€’12mo ago
Haha, touchΓ©!

Did you find this page helpful?