lwc
lwc copied to clipboard
Component constructors cannot be new-ed
Description
import Component from 'x/component'
lwc.createElement('x-component', { is: Component })
new Component() // throws "Illegal constructor"
Maybe this is working as designed, but it seems a bit surprising (at least to me).
Steps to Reproduce
-
new Component()
throwsIllegal constructor
fromLightningElement
Expected Results
No error thrown
Actual Results
Version
LWC 2.21.1
Possible Solution
Use lwc.createElement
instead.
This issue has been linked to a new work item: W-11533536
Updated the description, I was wrong about CustomElementConstructor
. Forgot that you can't new
those until the elements are defined.
I think it may be intentional but I'm not 100% sure. @caridy might know.
It is intentional. Basically, what should be the result of new Component()
? Should that return an element? it can't be, because that's not exactly a custom element constructor. should that return a component instance then? yes, but what can you do with a component instance? and how do you create the associated element? and what will be the tagName of the associated element? A component to function needs to have a 1-1 relationship with an element, otherwise it is useless.
@caridy Thanks, that makes sense. I guess in my head, I was thinking that lwc.createElement()
is roughly analogous to customElements.define()
, so in that way, you "should" be able to do new Component()
after the element has been defined, because it does have a tag name associated with it, and it's registered in customElements
.
I'm not totally convinced this is something we need to support, though. That's why we have CustomElementConstructor
.
I'm not totally convinced this is something we need to support, though. That's why we have
CustomElementConstructor
.
Exactly, you don't do new Component()
instead you do new Component.CustomElementConstructor()
, and of course, you must register it before you do that, and as a result, you get an element.
Probably a different aspect of the same issue: you can manually create an element called x-foo
that has already been claimed by LWC, but the element will not be upgraded. https://github.com/salesforce/lwc/issues/2984
Well, with #2724 that's not longer observable... so creating x-foo that was claimed by LWC via a pivot is not visible to user-land code, so for them creating that element will NOT upgrade it and will not give you any hint that it is indeed already registered, and a definition after that will in fact upgrade the manually created element.
Pivots are still observable, just not 100% observable. If you do element.constructor.toString()
you won't get HTMLElement() { [native code] }
as you would with a non-upgraded element, so it is observable that it's a custom element. However, it won't be upgraded in the sense that it has a shadowRoot, has reactive properties, etc.
element.constructor.toString()
should never be native code if the element is a custom element. I don't understand.
My point is that if we do this:
- Create a scoped element (within LWC) named
x-foo
- Outside of LWC, someone calls
document.createElement('x-foo').constructor.toString()
, which returns'class extends HTMLElement {\n}'
- Whereas if
x-foo
were never defined, then it would return'function HTMLElement() { [native code] }'
So in that sense, the pivots are "observable."
I see! maybe we can make it a class considering that in IE11 that's the case anyways... so we can target the path of patching to use a class instead of an actual function. What do you think?
@caridy I'm not sure I understand. We already do use a class, not a function:
https://github.com/salesforce/lwc/blob/da490791c4dc4dcb476b541c7a0dc7abfa9967ad/packages/%40lwc/engine-core/src/framework/upgradable-element.ts#L37
We can discuss offline if it's easier. 🙂