Warning: Maximum update depth exceeded on basic example
See original GitHub issueHey man, great lib in general, some cool features here!
I’m trying to get the basics right and I’m having trouble with the editor behaving in this unusual way. As soon as I connect any two nodes even with the basic example here: https://flume.dev/docs/basic-config, I start getting this error non stop:
react_devtools_backend.js:4026 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render. at NodeEditor (http://localhost:3000/static/js/bundle.js:10660:30) at div at App
I’ve compiled it by running the latest create react app, this is how my package.json looks like:
"packages": {
"": {
"name": "cpq-actionprocess",
"version": "0.1.0",
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"flume": "^0.8.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
},
Hope this is useful.
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:13 (1 by maintainers)
Top Results From Across the Web
React warning Maximum update depth exceeded
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency ...
Read more >Fix the "Maximum Update Depth Exceeded" Error in React
One quick solution is to move the function inside the useEffect hook. function App() { const [views ...
Read more >Maximum update depth exceeded warning in React
Maximum update depth exceeded warning in React ... This warning can often happen when a component sets the state inside the useEffect hook....
Read more >How to Fix Maximum Update Depth Exceeded in React and ...
In this video, I show you several ways the dreaded " Max Update Depth Exceeded " error commonly occurs in React and React...
Read more >React - maximum update depth exceeded - CodeProject
You need to share the code, specifically the line raising the error. Or else the answer to your query is already shared: "This...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I believe I found the high-level cause and root cause of the issue.
High-level cause
The generic cause is this: Flume doesn’t support out-of-the-box React 18’s new concurrent mode (and it doesn’t advertise it does, as it has React 17 in its peer dependencies, not React 18). Using the minimal example from the previous post, I could alternate between the sample crashing or not depending wether I used
ReactDom.render()
(legacy mode even in React 18, does not trigger the crash) andReactDom.createRoot().render()
(concurrent mode, triggers the crash).As such, there is a first workaround for users currently experiencing this issue: if at all possible, switch back to using legacy root creation, which will disable concurrent mode and prevent the issue from happening. You can find the API doc here: https://reactjs.org/docs/react-dom.html#render If/when the root cause is fixed in Flume, you’ll be able to go back to the current “concurrent” mode.
Root cause
The issue is caused in the NodeEditor component, having a LayoutEffect hook depending on a state variable (
shouldRecalculateConnections
), and then setting a new value for that state, combined with having a “manual” trigger function also being called in an effect deeper in the render tree (IoPorts.js/Input
). I can’t wrap my head around a precise, step-by-step trace of exactly what happens, but it seems to me like having an effect depending on a state it also sets, and having that same state set to something else in another render, provokes this.You can find the concerned code here: https://github.com/chrisjpatty/flume/blob/0372e92e65065772234ab83c2a106da43279915a/src/index.js#L97:L106
However, since we know the state dependency is what causes the infinite recursion, we can remove or replace that state to prevent the issue from happening.
Fix
Note: I don’t believe any of the two suggestions below would break on React < 18, which is important while the official peer dependency is React 17.
Depending on the intended behavior (which I couldn’t conclude on my own based on the code), I see two options:
Option 1: completely remove the
shouldRecalculateConnections
stateThis is the option I believe is less likely to be the intended behavior.
One option is to remove the
shouldRecalculateConnections
state, and simply callrecalculateConnections()
every time we want, instead of depending on an effect to do that calculation.The resulting code is as follows:
When tracing calls locally,
triggerRecalculation
works like before, since it previously setting the state resulted in a recalculation. However, the layout effect now callsrecalculateConnections
more often, since it doesn’t have a quick break when the state var would previously bypass the calculation. It seems to be called every time a node changes, so not that often, but still.Option 2: replace the state with an “initial did X” ref
This is the option I believe closest matches the current behavior.
The other option is based on the fact that only
triggerRecalculation
ever setsshouldRecalculateConnections
to true, thus causing a recalculation in the effect hook. I also see that the initial value ofshouldRecalculateConnections
is true, which means that on initial render, the recalculation will also be called.If the intended behavior is “recalculate on first render, then on every trigger”, then a ref var can be used instead of state to accomplish this, as follows:
I tested both options, and both prevented the crash from happening.
Of course, my knowledge of the code base and its intricacies is limited, and I could fail to see an issue with the suggestions above, but from my perspective, it seems to work as the original.
Workarounds
Any one of these can fix the issue:
Switch back to non-concurrent React
As I mentioned above, one workaround is to opt-out of React’s current “concurrent” mode, by switching to
ReactDom.render()
instead ofReactDom.createRoot().render()
.Use
patch-package
For users of
patch-package
, I also wrote the following patch, which applies “Option 2”, if you believe it is sufficient for your needs:The patch fixed the issue locally.
Final notes
If the library’s owner accepts pull requests, I will be happy to open one with the fix in option 2 (or something else if you have comments). As the fix is quite simple, you might also prefer to make the changes yourself.
And, thanks, by the way, for this awesome library 🙂
It looks like the recursion takes place in
src/components/IoPorts.js
. I’ve logged all the effects in this file and it looks like the recursion takes place inside the useEffect on lines 119-123.The variable
isConnected
is false andprevConnected
is true when the function is called based on dependencies. I don’t know what this code does, but removing it doesn’t seem to cause any collateral damage, at least in the tests I’ve done.I’ll do more tests tomorrow, but @chrisjpatty , can you help me figuring out what this code snipped does?