Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Further update children re-rentrancy problems. #315

Merged
merged 14 commits into from
Aug 11, 2021

Conversation

ConorGriffin37
Copy link
Member

@ConorGriffin37 ConorGriffin37 commented Aug 5, 2021

See the previous pull request here:
#301

Originally I had thought that this issue was specific to events but it can actually also be caused using callbacks. We recently ran into this issue again and the original fix for this did not fix the issue because it was caused by callbacks and not events.

Basically, the issue is that nodes lower down the Roact tree can change outside of their standard lifecycle and then cause a callback or event to a node higher up the tree. That component can re-render while the node further down the tree is updating its children, causing duplicates of elements to be added.

I added a check to guard against reentrancy in updateChildren and if updateChildren has been rerentered any extra nodes that have been created and are no longer needed will be unmounted.

Checklist before submitting:

  • Added entry to CHANGELOG.md
  • Added/updated relevant tests
  • Added/updated documentation

@coveralls
Copy link

coveralls commented Aug 5, 2021

Coverage Status

Coverage decreased (-0.2%) to 94.775% when pulling cb0684c on ConorGriffin37:updateChildrenReentrancy2 into d4f4a7a on Roblox:master.

@ConorGriffin37 ConorGriffin37 marked this pull request as ready for review August 5, 2021 20:09
Copy link
Contributor

@ZoteTheMighty ZoteTheMighty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments about the structure of the solution. Is the wasUnmounted logic a fix for something that was already happening? Or is it an additional solution to a new bug that happens when the rest of this new logic is enabled?

src/createReconciler.lua Outdated Show resolved Hide resolved
src/createReconciler.lua Outdated Show resolved Hide resolved
src/createReconciler.lua Show resolved Hide resolved
src/createReconciler.lua Show resolved Hide resolved
if config.tempFixUpdateChildrenReEntrancy then
if not virtualNode.wasUnmounted then
unmountVirtualNode(virtualNode)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I take this to mean that the re-entrancy was, in some cases, causing the same node to be unmounted more than once, is that the case? Can we get a comment here that explains the scenario that might trigger it, the way we have with the other blocks you added?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment for this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this could be related to #235? Would it fix that issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this would fix that issue, seems to be more about extra renders than extra unmounts.

ConorGriffin37 and others added 2 commits August 9, 2021 13:27
Co-authored-by: Paul Doyle <37384169+ZoteTheMighty@users.noreply.github.com>
@ConorGriffin37
Copy link
Member Author

The wasUnmounted logic is a fix for something that could already happen. I was initially worried that double unmounting could be caused by this new logic (and still am) but it does actually already happen in the original bug.

Old way this could happen:
updateChildren happens for the first time
node is unmounted by call to updateVirtualNode() on line 84
due to event or callbacks triggered by this unmounting, another component higher up the tree starts re-rendering
updateChildren is renentered for the second time
node is unmounted again by another call to updateVirtualNode on line 84

Copy link
Contributor

@oltrep oltrep left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super familiar with Roact's internals but the change looks good to me

})

self.onDidMountCallback = function()
if self.state.count < 5 then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason why 5? To me it looks like onDidMountCallback should be called only once, so self.state.count should be 1 and then turn into 2. If that is correct then maybe we could add an explicit assertion here that shows that (i.e. expect(self.state.count).to.equal(1)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No specific reason why 5, just want to make sure there isn't a scenario where this test starts looping an unreasonable amount of times. Before the bug fix, onDidMountCallback would actually be called multiple times due to the duplicate components being mounted. I'll add the expect here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, onDidMountCallback is still called twice here. Basically, two different LowestComponent are still mounted so this is actually called twice. The first LowestComponent to be mounted is unmounted to prevent the duplication issue.


self.onDidMountCallback = function()
if self.state.count < 5 then
self:setState({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as above ⬆️ (or below, I don't know how github will order my comments 😄 )

if config.tempFixUpdateChildrenReEntrancy then
if not virtualNode.wasUnmounted then
unmountVirtualNode(virtualNode)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this could be related to #235? Would it fix that issue?

Copy link
Contributor

@ZoteTheMighty ZoteTheMighty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonably safe to me. Thanks for doing the legwork and pinning the changes with testing. Please take a look at @oltrep's comments, but I think the solution as implemented looks alright.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants