T
TanStackβ€’10mo ago
correct-apricot

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
xenophobic-harlequin
xenophobic-harlequinβ€’10mo ago
you mean router.invalidate() ?
fascinating-indigo
fascinating-indigoβ€’10mo ago
this question needs more detail which kind of params? path? search? using loader()?
correct-apricot
correct-apricotOPβ€’10mo 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.
fascinating-indigo
fascinating-indigoβ€’10mo ago
minimal example would be good
correct-apricot
correct-apricotOPβ€’9mo 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
correct-apricot
correct-apricotOPβ€’9mo ago
Navigate between the two posts. I'd like the thumb to reset between navigations, since it's local state to that page
fascinating-indigo
fascinating-indigoβ€’9mo ago
I see you want what most dont want πŸ˜› namely remounting
correct-apricot
correct-apricotOPβ€’9mo ago
Yes, exactly ^^ Is it doable? 😬
fascinating-indigo
fascinating-indigoβ€’9mo ago
everything is
correct-apricot
correct-apricotOPβ€’9mo ago
Ofc πŸ˜… But is there a builtin mechanism to get this behaviour atm?
fascinating-indigo
fascinating-indigoβ€’9mo ago
fascinating-indigo
fascinating-indigoβ€’9mo ago
function Wrapper() {
const params = Route.useParams();

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

return <PostComponent key={params.postId} />;
}
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo ago
it really depends
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo 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
correct-apricot
correct-apricotOPβ€’9mo ago
Huh okay interesting I wonder what use cases they had, because I struggle to come up with them myself
fascinating-indigo
fascinating-indigoβ€’9mo ago
so would a remountOnDepsChange do the trick for you ? (with a default on router)?
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo 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
correct-apricot
correct-apricotOPβ€’9mo ago
But the only way to pass search params into loader is via loaderDeps, right?
fascinating-indigo
fascinating-indigoβ€’9mo ago
yes why "but"?
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo ago
so you would need a separate way of describing your remount dependencies then
correct-apricot
correct-apricotOPβ€’9mo 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
xenophobic-harlequin
xenophobic-harlequinβ€’9mo ago
(would it be possible to make remountOnDepsChange a selector function ?)
correct-apricot
correct-apricotOPβ€’9mo ago
Preferrably on a global level, because I'd want that for all pages
fascinating-indigo
fascinating-indigoβ€’9mo ago
as I wrote above, this really depends on the individual app and interpretation
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo ago
getting that general purpose API right is always a challenge
correct-apricot
correct-apricotOPβ€’9mo ago
Yeah for sure! One of the trickiest parts about building a router I assume πŸ˜„
fascinating-indigo
fascinating-indigoβ€’9mo 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?
correct-apricot
correct-apricotOPβ€’9mo ago
Will do! The template picker directs me to discussions for feature requests. Still want it as an issue?
fascinating-indigo
fascinating-indigoβ€’9mo ago
discussions are ... crowded i don't like the interface
correct-apricot
correct-apricotOPβ€’9mo ago
Haha ok I'll make it an issue
fascinating-indigo
fascinating-indigoβ€’9mo 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?
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo 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
correct-apricot
correct-apricotOPβ€’9mo 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.
fascinating-indigo
fascinating-indigoβ€’9mo 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?
correct-apricot
correct-apricotOPβ€’9mo ago
No clue tbh. Haven't used React Router in a long time
fascinating-indigo
fascinating-indigoβ€’9mo 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...
fascinating-indigo
fascinating-indigoβ€’9mo 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
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo 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?
correct-apricot
correct-apricotOPβ€’9mo 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?
fascinating-indigo
fascinating-indigoβ€’9mo ago
I wouldnt go as far and say this is a matter of "correctness"...
correct-apricot
correct-apricotOPβ€’9mo 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
fascinating-indigo
fascinating-indigoβ€’9mo ago
so it's 2:1 😁
correct-apricot
correct-apricotOPβ€’9mo ago
Haha, touchΓ©!

Did you find this page helpful?