lwc icon indicating copy to clipboard operation
lwc copied to clipboard

Component constructors cannot be new-ed

Open nolanlawson opened this issue 1 year ago • 14 comments

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

Expected Results

No error thrown

Actual Results

Screen Shot 2022-08-01 at 4 16 05 PM

Version

LWC 2.21.1

Possible Solution

Use lwc.createElement instead.

nolanlawson avatar Aug 02 '22 15:08 nolanlawson

This issue has been linked to a new work item: W-11533536

git2gus[bot] avatar Aug 02 '22 15:08 git2gus[bot]

Updated the description, I was wrong about CustomElementConstructor. Forgot that you can't new those until the elements are defined.

nolanlawson avatar Aug 02 '22 16:08 nolanlawson

If this is the intended design should we add a KA and a section to the OSS / SFDC dev guides?

jmsjtu avatar Aug 04 '22 18:08 jmsjtu

I think it may be intentional but I'm not 100% sure. @caridy might know.

nolanlawson avatar Aug 04 '22 18:08 nolanlawson

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 avatar Aug 04 '22 20:08 caridy

@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.

nolanlawson avatar Aug 04 '22 21:08 nolanlawson

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.

caridy avatar Aug 04 '22 23:08 caridy

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

nolanlawson avatar Aug 10 '22 21:08 nolanlawson

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.

caridy avatar Aug 11 '22 20:08 caridy

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.

nolanlawson avatar Aug 11 '22 21:08 nolanlawson

element.constructor.toString() should never be native code if the element is a custom element. I don't understand.

caridy avatar Aug 12 '22 17:08 caridy

My point is that if we do this:

  1. Create a scoped element (within LWC) named x-foo
  2. Outside of LWC, someone calls document.createElement('x-foo').constructor.toString(), which returns 'class extends HTMLElement {\n}'
  3. Whereas if x-foo were never defined, then it would return 'function HTMLElement() { [native code] }'

So in that sense, the pivots are "observable."

nolanlawson avatar Aug 15 '22 15:08 nolanlawson

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 avatar Aug 18 '22 13:08 caridy

@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. 🙂

nolanlawson avatar Aug 22 '22 19:08 nolanlawson