React: Component from previous route are staying in memory while navigation to a different route.

Created on 20 Sep 2018  路  5Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?
bug

What is the current behavior?
I am using react-router for making a single page application which have multiple routes. While navigating between the routes, I noticed that the component from the previous route is still present in the memory e.g. If I navigate from route1 to route2, I can see that component corresponding to route1 is still in the memory. Moreover, if I change my route from route1 to route2 then route2 to route3, the component from route1 is cleared out and route2 component is still in the memory while I am at route3.

Adding heap snapshot for this unexpected behaviour :
react1

CodeSandbox demo for reproducing the same: Demo

What is the expected behavior?
Ideally, component corresponding to the previous route must be completely removed from the memory when we navigate to the new route. Or there must be some rule if we are keeping it for some optimizations.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
react 16.4.2
react 16.4.2
react-router-dom 4.3.1

Needs More Information Stale Needs Investigation

Most helpful comment

@gaearon So we investigated this . If you see the screenshot below, you would see that route 3 (test3) has a deep nested react components each with a huge state.

When I go to route 4 (test 4 - see the heap snapshot below) not only the root component but the nested child along with its state is there. As a result the route memory for test4 bloats up.

It gets only garbage collected in route 5. While debugging we could see (as you mentioned above) that during reconciliation it gets gc-ed while creating the next WIP tree when the previous fibers are used and effects(nextEffect, firstEffect, lastEffect) is wiped out. So the references to Test3 get removed at this line in ReactFiber.js

But the parent of Test3 which is the route component of type (茠 Route()) still keeps a reference to Test3 using its lastEffect. The lastEffect is reset when the fiber gets reused in the code pointed above.

So from the behaviour it seems, a fiber node even though its disconnected from parent , child and alternate using the detach fiber can still have references to it from other fibers using the effects array.

Also this is reproducible in the production version as well.
Here are the detailed Repro Steps

  • Load the sandbox
  • Navigate to Route 3 and take memory snapshot
  • Navigate to route 4 and take memory snapshot (All the route 3 components still persist)
  • Navigate to route 5 and take memory snapshot (All the route 4 components still persist, route 3 is gone)
  • Wait for 10 seconds for it trigger a state change automatically , and take memory snapshot (route 4 is gc-ed)

screen shot 2018-09-20 at 10 09 13 pm

All 5 comments

AFAIK it's expected that a component one level deep would be retained until the next update which would clear it out.

https://github.com/facebook/react/blob/e1a067dea0ffcacd1f664f30cd14463b00f52fa7/packages/react-reconciler/src/ReactFiberCommitWork.js#L497-L501

If you're seeing a deeper tree not getting GC'd, please make sure you're checking with a production version and include exact steps you were doing to diagnose it.

@gaearon So we investigated this . If you see the screenshot below, you would see that route 3 (test3) has a deep nested react components each with a huge state.

When I go to route 4 (test 4 - see the heap snapshot below) not only the root component but the nested child along with its state is there. As a result the route memory for test4 bloats up.

It gets only garbage collected in route 5. While debugging we could see (as you mentioned above) that during reconciliation it gets gc-ed while creating the next WIP tree when the previous fibers are used and effects(nextEffect, firstEffect, lastEffect) is wiped out. So the references to Test3 get removed at this line in ReactFiber.js

But the parent of Test3 which is the route component of type (茠 Route()) still keeps a reference to Test3 using its lastEffect. The lastEffect is reset when the fiber gets reused in the code pointed above.

So from the behaviour it seems, a fiber node even though its disconnected from parent , child and alternate using the detach fiber can still have references to it from other fibers using the effects array.

Also this is reproducible in the production version as well.
Here are the detailed Repro Steps

  • Load the sandbox
  • Navigate to Route 3 and take memory snapshot
  • Navigate to route 4 and take memory snapshot (All the route 3 components still persist)
  • Navigate to route 5 and take memory snapshot (All the route 4 components still persist, route 3 is gone)
  • Wait for 10 seconds for it trigger a state change automatically , and take memory snapshot (route 4 is gc-ed)

screen shot 2018-09-20 at 10 09 13 pm

So from the behaviour it seems, a fiber node even though its disconnected from parent , child and alternate using the detach fiber can still have references to it from other fibers using the effects array.

I think this makes sense. Thanks for explaining.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

Was this page helpful?
0 / 5 - 0 ratings