0.33.7 causes infinite reactivity loops with any custom elements that happen to read a reactive variable in their constructor
See original GitHub issueBefore 0.33.7, things worked fine in my case because reading a reactive variable in a constructor wasn’t triggering reactivity due to the use of cloneNode
inside of dom-expression’s get children
getter.
Code is similar to this:
@element('my-el')
class MyEl extends Element {
@attribute foo = "bar" // @attribute creates a signal-backed property
constructor() {
this.somethingElseWithInitialValue = this.foo + "baz" // infinite loop, triggers `get children` again
}
}
A workaround for me for now was to downgrade to 0.33.0
. I’ll need to update elements to ensure they don’t read reactive properties in their constructor (easy thing to do, especially in cases where some initial logic depends on them).
Would it make sense to change the compiler output to the following with untrack
(or some other way to make the importNode
call untracked)?
const _el$123 = untrack(document.importNode(_tmpl$123))
The infinite loop re-runs the children
getter repeatedly.
Issue Analytics
- State:
- Created a year ago
- Comments:5 (5 by maintainers)
Top Results From Across the Web
Using custom elements - Web Components | MDN
This is just a simple example, but there is more you can do here. It is possible to define specific lifecycle callbacks inside...
Read more >Asynchronous assignment operation inside reactivity ... - GitHub
It does indeed seem that any variable assignment happening inside a setTimeout as part of a reactivity statement will cause an infinite loop...
Read more >Custom Element Best Practices - web.dev
Custom elements let you construct your own HTML tags. This checklist covers best practices to help you build high quality elements.
Read more >Lifecycle Hooks in Web Components - Ultimate Courses
Custom Element Reactions are called with special care in order to prevent user's code from being executed in the middle of a delicate...
Read more >Making Web Components reactive - HorusKol
This part is all about making our Web Component reactive, ... One thing that is common to all is how we define our...
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
This sounds analogous to
createComponent
wrapping component functions inuntrack
. I don’t know WC well enough to say for sure, but it sounds like a good idea.This is exactly the problem Custom Element authors need to write robust code against. Custom Element authors need to fix this issue in their custom elements (making them robust against upgrade timing) regardless if Solid side steps the issue in this particular case or not.
The thing about this fix, is that it “fixes” this issue only for people who are writing custom elements and testing them only inside of Solid components.
The moment any other non-Solid app writes values to a pre-upgraded custom element for any reason (it will happen!) Solid’s change to
importNode
doesn’t come into play at all.Whoever this fix is for, they need to be aware that they haven’t fixed the problem, and that this issue is only side stepped while they’re testing in Solid.
I performed the
untrack
experiment we chatted about in the Discord chat.First I’ll describe the problem, then show the workaround. This is the problematic code:
This triggers this effect in Solid’s
Show
component in my case:The call to
props.children
callsimportNode
which creates a new element, and that element subsequently reads and writesfoo
within the same memo effect of the Show component.Each time an element is created, it is a new
.foo
property with a new signal behind it. So it loops infinitely, although the signal is different on each effect run.To work around the problem on my end, I can do this:
Now it no longer loops.
I could abstract that workaround into the
@reactive
decorator like so:but with the
@element
decorator it is much more difficult because we need to avoid theIllegal constructor
error from the DOM engine.best solution:
I think we should wrap the
importNode
call inuntrack
. Here’s why:With DOM APIs custom elements are always constructed in a non-reactive context and do not track any dependencies (because the DOM engine doesn’t use Solid). Therefore I think that Solid should follow the pattern of custom element construction not being reactive with
untrack
aroundimportNode
.