react-spring: Memory leak in react-spring v9 when used with SSR

🐛 Bug Report

React Spring’s useSpring is leaking memory when used on server. For every request that renders a react page that uses useSpring hook I can see an approximate increase in 10000 lines of compile code, string and array getting increased in the same proportion.

To Reproduce

Go to the root folder of the project and install all the deps run yarn build:local in a terminal window run yarn serve:local in a terminal window

use ab -n 10000 -c 2 http://localhost:3000/

to send huge traffic to the server.

Either take the heap dump or see the memory memory status printed in the console.

Expected behavior

I expect the memory to be reclaimed by the GC to restore normal operations of the server

Link to repro (highly encouraged)

https://github.com/thebedroomprogrammer/react-spring-debug

Environment

  • react-spring v9.1.2
  • react v17.0.2

Screenshot 2021-04-24 at 4 57 01 PM

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

Thanks for the investigation!! No one else has really raised an issue, so I am going to close this. But we can always reopen and take another look if needs be.

Did a bit of digging into @thebedroomprogrammer’s example, ended up finding similar results to @joshuaellis. Bumping to react-spring v9 (specifically 9.2.3) dramatically helped so I did all of my testing with that.

My benchmark procedure was:

  1. Start the server (yarn serve:local), should already run with --inspect
  2. Open Chrome DevTools (chrome://inspect), select the Node process and navigate to Memory for heap snapshotting
  3. Remove the setInterval logger in src/index.tsx (no longer needed)
  4. Restart the server and monitor the total memory usage. Seems like there’s some upfront garbage collecting going on so I waited for that to plateau before taking my first snapshot
  5. Run a stress test (ab -n 10000 -c 2 http://localhost:3000/), take a snapshot immediately after
  6. Wait 1 minute for garbage collecting to do some cleanup, then take a final snapshot

All testing was done on Node v14.17.0. These were my results:

State Usage Diff
Start 8.5 MB
Post stress 9.8 MB +1.3 MB
Post stress +1m 9.4 MB +0.9 MB

This already seems negligible, but I was curious how much of this was actually react-spring related. I removed any react-spring references (swapped out animated.div for a vanilla div) and got these results:

State Usage Diff
Start 5.4 MB
Post stress 6.4 MB +1.0 MB
Post stress +1m 6.1 MB +0.7 MB

Given that, I’d say this isn’t worth investigating unless there’s a larger scale example repo that exaggerates the problem.

@joshuaellis I was following this docs example, but React Spring tries calling the onReset callback when the component has already been unmounted (user navigates away from the Next.js page).

Demo: https://codesandbox.io/embed/magical-field-k7vg9?fontsize=14&hidenavigation=1&theme=dark (Navigate between the Next.js pages)

I found a quick workaround for this by using a ref instead of state:

const toggle = useRef(false);
...
onReset: () => { toggle.current = !toggle.current }