Skip to content
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

Calling super() on HTMLElement will throw illegal constructor exception #812

Open
MJomaa opened this issue Mar 7, 2023 · 11 comments
Open

Comments

@MJomaa
Copy link

MJomaa commented Mar 7, 2023

Issue
You cannot extend an HTMLELement and call super() in the constructor.
I get an illegal constructor exception when I use emoji-mart in a NextJS application. First render on the server is fine, but second render on the client (navigate back and forth) will throw an exception.

Code Location
https://github.com/missive/emoji-mart/blob/main/packages/emoji-mart/src/components/HTMLElement/HTMLElement.ts#L15

Reproduction
https://www.typescriptlang.org/play?#code/MYewdgzgLgBA6gSzAExAdwBIBUCyAZAUQBsBTAWxLFgF4AoGGKATwAcSQAzGNJVNGAITVqMAOQBXFCQ5ISyUTABki7r3QA6bPmLlKUegxgB+VSg1bCpClQMMAXDADyAIwBWJYPtrAiAQwgQMBY61lBYJNAwJAAeUJTIgYhmmLiWulQwAN4GoJBQAE7iniD5ABQAlFm2MBDibGXlANwwAPQtMAA8HTAAkkSkAOa+RDC50IXF+QYAvrQGqBUOAG4gCMhVhqPgECCk6kQgA6WiGCT9IPAlRPJNM7Sz3tuwcZEiYCT8wVZ64dAVjbQXlB1AsmkA

const WindowHTMLElement =
  typeof window !== 'undefined' && window.HTMLElement
    ? window.HTMLElement  // should not extend this
    : Object

class HTMLElementTest extends WindowHTMLElement {
  constructor() {
    super(); // Illegal constructor
  }

  do(): void {
    console.log('Hello World');
  }
}

const test = new HTMLElementTest();
test.do();
@xmsz
Copy link

xmsz commented Mar 17, 2023

any update?

f2c-ci-robot bot pushed a commit to halo-dev/plugin-comment-widget that referenced this issue Mar 29, 2023
修复 Emoji 选择组件无法正常初始化的问题。

原因是因为原来使用的 Emoji Mart 库可能会因为多次实例化导致异常,目前暂无解决方案,详情可查阅:missive/emoji-mart#812

目前改为使用 https://github.com/serebrov/emoji-mart-vue-test 代替。

Fixes #7
Ref #8 (comment)

```release-note
修复 Emoji 选择组件无法正常初始化的问题。
```
@MJomaa
Copy link
Author

MJomaa commented Apr 1, 2023

I think the fix would be to register the custom element so "super()" and "this" can be used.

@forresto
Copy link

register the custom element

This does seem to help with the specific error:

import { Picker } from "emoji-mart";
customElements.define("emoji-picker", Picker);

@MJomaa
Copy link
Author

MJomaa commented May 16, 2023

For me the temp fix was:

import { Picker } from 'emoji-mart';
if (
  typeof customElements !== 'undefined' &&
  !customElements.get('em-emoji-picker')
) {
  customElements.define('em-emoji-picker', Picker as any);
}

@redstar08
Copy link

redstar08 commented May 16, 2023

@MJomaa
Unfortunatly, it's not work, I try it just now, still throw Uncaught TypeError: Illegal constructor error.

@MJomaa
Copy link
Author

MJomaa commented May 16, 2023

In case you use React: Actually wouldn't it be easier to just NOT use @emoji-mart/react but our own wrapper?

I mean this is all the code for the wrapper:
https://github.com/missive/emoji-mart/blob/main/packages/emoji-mart-react/react.tsx

// @ts-nocheck
import React, { useEffect, useRef } from 'react'
import { Picker } from 'emoji-mart'

export default function EmojiPicker(props) {
  const ref = useRef(null)
  const instance = useRef(null)

  if (instance.current) {
    instance.current.update(props)
  }

  useEffect(() => {
    instance.current = new Picker({ ...props, ref })

    return () => {
      instance.current = null
    }
  }, [])

  return React.createElement('div', { ref })
}

What if we just added

if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
    if (!customElements.get('em-emoji-picker')) {
        customElements.define('em-emoji-picker', Picker);
    }
    instance.current = new Picker({ ...props, data, ref });
}

inside the useEffect() before creating a new Picker?

@redstar08 Can you try to create a new file "emoji-picker.tsx" and use this?

// @ts-nocheck
import { Picker } from 'emoji-mart';
import React, { useEffect, useRef } from 'react';

export const EmojiPicker = (props) => {
  const ref = useRef(null);
  const instance = useRef(null);

  if (instance.current) {
    instance.current.update(props);
  }

  useEffect(() => {
    if (
      typeof window !== 'undefined' &&
      typeof window.customElements !== 'undefined'
    ) {
      if (!customElements.get('em-emoji-picker')) {
        customElements.define('em-emoji-picker', Picker);
      }
      instance.current = new Picker({ ...props, data, ref });
    }
    return () => {
      instance.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return React.createElement('div', { ref });
};

@MJomaa
Copy link
Author

MJomaa commented May 25, 2023

Switched to https://github.com/ealush/emoji-picker-react and seems like mattermost [1] did the same.

[1] mattermost/mattermost-plugin-calls#294

@michelson
Copy link

we have the same issue. we are using this from CDN and it works for the first load, but I we try to initialize it again it fails

@akshatgautam-ct
Copy link

Are there any updates with regards to this? For me, the same code works at one place but fails at another.

@bybash
Copy link

bybash commented Jun 18, 2024

I know it is quite late but here is my solution, because an element is custom-defined, calling the instructor will throw an Illegal constructor. You need to use it directly after you register it.
So I used createElement with "em-emoji-picker" element and passed the props. Depending on my needs, I needed to update props, after it was rendered, so I added a 100ms delay to update the function and make it work.

import { type Picker } from "emoji-mart";

// @ts-expect-error no-def
function EmojiPicker(props) {
  const ref = useRef<Picker>(null);
  useEffect(() => {
    let current = ref.current;
    if (current && props) {
      setTimeout(() => {
        current?.update(props);
      }, 100);
    }
    return () => {
      current = null;
    };
  }, [props]);

  return React.createElement("em-emoji-picker", {
    ...props,
    ref,
  });
}

@Donggggg
Copy link

I made a PR(#958) to fix this issue.

Add a guard logic to reuse custom element that is already defined.

 useEffect(() => {
    const PickerElement = window?.customElements.get('em-emoji-picker')
    if (PickerElement) {
      instance.current = new PickerElement({ ...props, ref })
      return
    }
    instance.current = new Picker({ ...props, ref })
   ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants