-
Notifications
You must be signed in to change notification settings - Fork 392
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: consistent disconnectedCallback ordering in rendering VFragments #3369
Conversation
.then(() => { | ||
// disconnect x-shadow-parent + | ||
// connect x-shadow-container with 2 parents, 'a' and 'b' | ||
expect(window.timingBuffer).toEqual([ |
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.
I checked the ordering here in both synthetic and native shadow against if:true and lwc:if:
if:true - synthetic - 654321
lwc:if - synthetic - 654321
if:true - native - 123456
lwc:if - native - 654321
In synthetic, both if:true and lwc:if are managed by runChildNodesDisconnectedCallback
, which uses reverse ordering.
In native, if:true is managed by resetComponentRoot
, which uses forward ordering.
lwc:if
in native cannot match both the old if:true
behavior (forward) and the runChildNodesDisconnectedCallback
behavior (reverse). From #2713 it sounds like forward is supposed to be the "correct" behavior, and that's the behavior when conditionals aren't used at all.
IMO since if:true
already has different behaviors between synthetic and native, doing the same for lwc:if
shouldn't be introducing any confusion that didn't exist before. Keep it consistent (reverse) for synthetic, and correct (forward) for native:
if:true - synthetic - 654321
lwc:if - synthetic - 654321
if:true - native - 123456
lwc:if - native - 123456
Do you see any problems with this approach?
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.
Note that it would not be too difficult to update lwc:if to use forward ordering; it already goes through resetComponentRoot
which already does so.
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.
Conclusion from Dev Sync:
if:true - synthetic - 654321
lwc:if - synthetic - 654321
if:true - native - 123456
lwc:if - native - 123456
This will help match native, browser behavior
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.
lwc:if - synthetic - 654321
This is a bit sad, but I would not go out of our way to fix it, since it's synthetic.
}) | ||
.then(() => { | ||
expect(window.timingBuffer).toEqual([ | ||
'leaf:T2-6:disconnectedCallback', |
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.
This step, hiding the content, is interesting because it doesn't follow the same code path as when the template is swapped.
Again, I checked the ordering in both native and synthetic:
if:true - synthetic - 654321
lwc:if - synthetic - 123456
if:true - native - 654321
lwc:if - native - 123456
Here, if:true
is always managed by updateStaticChildren
, which goes in reverse order.
lwc:if
is always managed by unmountVNodes
, which goes in forward order.
I believe it's too late and risky to try to change the behavior for if:true
, so we have to decide whether we want lwc:if
to have the correct forward behavior in both synthetic and native, or figure out how to change it in synthetic to be consistent.
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.
Note that changing the ordering for lwc:if here would be difficult since it goes through unmountVNodes
.
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.
Conclusion from dev sync: leave as-is, as we don't guarantee any expectations around ordering of disconnectedCallback when it comes to sibling nodes.
packages/@lwc/integration-karma/test/rendering/callback-invocation-order/index.spec.js
Show resolved
Hide resolved
This PR and its comments are related to the issue brought up in #3363 (comment). Note that I have not included a fix yet because the desired behavior hasn't been confirmed. @nolanlawson @ravijayaramappa please take a look when you get the chance. |
// disconnect x-shadow-parent + | ||
// connect x-shadow-container with 2 parents, 'a' and 'b' | ||
expect(window.timingBuffer).toEqual( | ||
window.lwcRuntimeFlags.ENABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE |
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.
Updated for the desired behavior in native lifecycle vs synthetic lifecycle.
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.
#3369 (comment) for details
}) | ||
.then(() => { | ||
expect(window.timingBuffer).toEqual([ | ||
'leaf:T2-1:disconnectedCallback', |
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.
Updated per #3369 (comment).
if (!isNull(vnode)) { | ||
// VFragments are special; their .elm property does not point to the root element since they have no single root. | ||
if (isVFragment(vnode)) { | ||
recursivelyRemoveChildren(vnode.children, vm); |
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.
Switching back to recursion for forward traversal. Matches the traversal behavior of recursivelyDisconnectChildren
in line 708.
// Helper function to remove all children of the root node. | ||
// If the set of children includes VFragment nodes, we need to remove the children of those nodes too. | ||
// Since VFragments can contain other VFragments, we need to traverse the entire of tree of VFragments. | ||
// If the set contains no VFragment nodes, no traversal is 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.
I'd add a comment here noting that we are going in forward-tree order to match native disconnectedCallback
ordering when removing a top-level node.
} | ||
} | ||
} | ||
recursivelyRemoveChildren(vm.children, vm); |
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.
Love seeing so much red!
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.
I am surprised that this does not introduce an observable change to if:true
. But if it doesn't, then let's go ahead. 👍
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.
It's probably because the logic is overall still the same for if:true
. We went from:
for (let i = 0, len = children.length; i < len; i++) {
const child = children[i];
if (!isNull(child) && !isUndefined(child.elm)) {
remove(child.elm, renderRoot);
}
}
to
for (let i = 0, len = vnodes.length; i < len; i += 1) {
const vnode = vnodes[i];
if (!isNull(vnode)) {
if (isVFragment(vnode)) {
recursivelyRemoveChildren(vnode.children, vm);
} else if (!isUndefined(vnode.elm)) {
remove(vnode.elm, renderRoot);
}
}
}
No existing use cases should go down the new recursive path we've added.
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.
I'm cool with this as long we are not changing the behavior of if:true
in any context. lwc:if
is new enough that we can probably safely break it with impunity.
Long-term, any other discrepancies should be fixed by #3352 plus migrating to native shadow.
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.
LGTM
…#3369) * fix: add integration tests * fix: update and fix ordering
Details
Fixes an issue with VFragments and inconsistent disconnectedCallback ordering.
Note that because of #2713, there will inherently be some inconsistency in the disconnectedCallback ordering for this use case.
Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
GUS work item