-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix components instantiated twice because of complex mutations (#2376)
* Add failing test * Fix component instantiated twice because of complex mutations * Update comment
- Loading branch information
Showing
2 changed files
with
49 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,13 +160,33 @@ function onMutate(mutations) { | |
onAttributeAddeds.forEach(i => i(el, attrs)) | ||
}) | ||
|
||
// Mutations are undled together by the browser but sometimes | ||
// for complex cases, there may be javascript code adding a wrapper | ||
// and then an alpine component as a child of that wrapper in the same | ||
// function and the mutation observer will receive 2 different mutations. | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
SimoTod
via email
Author
Collaborator
|
||
// when it comes to run them, the dom contains both changes so the child | ||
// element would be processed twice as Alpine calls initTree on | ||
// both mutations. We mark all node as _x_ignored and only remove the flag | ||
// when processing the node to avoid those duplicates. | ||
addedNodes.forEach((node) => { | ||
node._x_ignoreSelf = true | ||
node._x_ignore = true | ||
}) | ||
for (let node of addedNodes) { | ||
// If an element gets moved on a page, it's registered | ||
// If an element gets moved on a page, it's registered | ||
// as both an "add" and "remove", so we want to skip those. | ||
if (removedNodes.includes(node)) continue | ||
|
||
delete node._x_ignoreSelf | ||
delete node._x_ignore | ||
onElAddeds.forEach(i => i(node)) | ||
node._x_ignore = true | ||
node._x_ignoreSelf = true | ||
} | ||
addedNodes.forEach((node) => { | ||
delete node._x_ignoreSelf | ||
delete node._x_ignore | ||
}) | ||
|
||
for (let node of removedNodes) { | ||
// If an element gets moved on a page, it's registered | ||
|
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
I have met the same behavior for replacing elements ( or moving element in DOM ): I'm using liveview
0.17.7
and when it processeslive_redirect
it replaces main element in DOM with new one, copies some old (sticky
) elements into new main element, applies some diffs to copied elements.So when there are a lot of other mutations applied, replaced element tree becomes destroyed, cause one of
removeNode
mutation comes non bundled withaddedNode
and a checkif (addedNodes.includes(node))
is not working.So for complex mutations, it’s not safe to rely on mutation bundling.
Probably it somehow related with
isCollecting=true
state, when some mutations are bundled by alpine and some are processed directly from MutationObserver whenisCollecting
becomes false. In any case, it would be nice to not affect native bundling of MutationObserver.I'm not sure how to reproduce this, so I'm just leaving my observations, and probably they could help you understand a problem better.
By the way, why does alpine need
destroyTree
forremovedNodes
? ProbablyremovedNode
could be just marked as destroyed and real destroy could be deferred. When deferred destroying would be called it will double-check that node is not existing in DOM ...