-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix maximum update depth exceeded #387
Open
EdoardoGruppi
wants to merge
6
commits into
iddan:master
Choose a base branch
from
EdoardoGruppi:fix-maximum-update-depth-exceeded
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
66347d8
Add function to deeply compare arrays
fcf2a53
Fix bug (infinite render loop) caused by wrong dependence arrays
4bab637
Improve performance by using a new primitive variable to trigger events
845b5c4
Update test to handle date and milliseconds for the setCellData action
6432922
Remove redundant renderings
5a24c81
Fix rendering issue: table size
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Iddan,
First off, it's commonly advised against including objects directly in the dependency array of a
useEffect
. This is because objects are compared by reference, not by value. What this means is that even if the content of the object hasn't changed, if a new object is created on each render, the effect will still run on every render because the references are different.So, although comparing object references directly might seem like a good idea, it's actually not recommended.
In the case of react-spreadsheet, the code below can lead to an infinite loop due to shallow comparisons made within the useEffect body and the dependency array.
To illustrate this point further, you can try changing the second block to:
You'll notice that even though the object might appear to be the same instance, its reference changes after every rendering. This causes the console log to be called every time there is a modification.
To summarize:
state.model.data
changes its reference every timesetCellData
is called.useEffect
body is executed.if
condition is verified as the reference is always different from the one previously stored.if
condition is verified because there is no guaranteeprops.data
has a reference equal to the newstate.model.data
reference.onChange
function calls thesetData
function within theSpreadSheet
object inApp.tsx
.Solution
Adding a new
lastUpdateDate
property addresses this issue while avoiding resource-intensive deep nested comparisons.With this addition, we track the last time
setCellData
is executed. If the data displayed in the UI has a differentlastUpdateDate
value, theuseEffect
is called, and the displayed information is updated accordingly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use just a string compare? For the huge datasets, it might be too slow though, but there is other libraries that can handle stringifying of an object more performant. Comparing a string, instead of having to traverse a huge object-graph is way easier.
The lastUpdate date check can probably cause some bad sideeffects as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @EdoardoGruppi for the detailed writing. I am unaware that any official React resources do not recommend object reference comparison. The real problem is the array reference changes which I don't see why it should happen more than once per mutation to the data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@EdoardoGruppi are you using the latest version of the code? The controlled storybook story works correctly and it uses the same code as you posted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thsorens in the same spirit of React, I try to avoid unsimple comparisons on data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @iddan,
thanks for your responses.
I confirm that I am using the latter version of the code. To be 100% sure, I re-did the test by cloning the repo from scratch, but nothing changed. You can find a sandbox at this link to check the results I am getting. To see the logs, you just need to open the console in the embedded CodeSandbox browser.
You're correct that React doesn't explicitly advise against using non-primitive values inside the dependency array. However, many developers recommend avoiding this practice because it can lead to mistakes and unintended re-renders.
I am confident that this is the exact problem with the react-spreadsheet library. In one of my initial commits, I used a function to deep compare objects, which solved the issue. The same error can be highlighted when adding a log inside the outer if statement. The comparison
state.model.data !== prevDataRef.current
returns true endlessly every time a cell changes its value.It's worth mentioning that I later replaced that function to avoid slowdowns with a comparison made on a new lastUpdatedDate property of the state object. I think that using the date is a nice way to solve the issue since: it doesn't add much code or memory occupation, makes the comparison very fast, and provides additional information that may be useful for future features.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iddan : I also found a different weird behavior in the storybook version. Add some random text in a cell. Put the focus on another cell. Then go back to the cell, type 1 (the number 1). The focus will automatically disappear, so it will not be possible to continue writing in the cell. It might be related to the grid running in storybook, since i cant reproduce it in the "demo" version. (Running chrome)