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

feat: resolve WebComponent constructor within h() #704

Closed
Hotell opened this issue May 22, 2017 · 9 comments
Closed

feat: resolve WebComponent constructor within h() #704

Hotell opened this issue May 22, 2017 · 9 comments

Comments

@Hotell
Copy link

Hotell commented May 22, 2017

As you know https://github.com/skatejs/skatejs 5.x is using Preact as default renderer ( we switched from Incremental DOM )

What would be awesome, is to add support for resolving custom-elements constructors within h so we can provide functionality like in v4:

import {
 h, // h is re-exporeted Preacts h
 Component // skate WebComponent composed mixin
} from 'skate'
import { MyUserCard  } from './web-components'
import { store } from './store'

class MyCmp extends Component {
  static get is(){ return 'my-cmp' }
  
  connectedCallback(){
     this.data = store.get('user')
  }
  
  renderCallback(){
    return (
        <div>
           <h1>Hello World</h1>
            <MyUserCard user={this.data} /> 
        </div>
     )
  }
}

window.customElements.define( MyCmp.is, MyCmp )

so <MyUserCard user={this.data} /> will be recognised by h that it's a custom element and will output -> <my-user-card></my-user-card>

This feature would also add better support to raw WebComponents which as win win for everyone #useThePlatform

@developit
Copy link
Member

@Hotell I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?

I can't remember if I've suggested it before, but would it be feasible to do something like this in skate?

There'd be two options there - the first is to provide that functionality via your re-exported h(), instead exporting a wrapper:

import { createElement } from 'preact';
// (in 8.x createElement is an alias of h, using it here to avoid name conflict)

export function h(nodeName, ...args) {
  if (typeof nodeName==='function' && typeof nodeName.is==='string') {
    nodeName = nodeName.is;
  }
  return createElement(nodeName, ...args);
}

Option 2 would be to use preact's hooks to inject the Web Component constructor swapping into anything using Preact, even non-skate things:

import { options } from 'preact';

let oldHook = options.vnode;
options.vnode = vnode => {
  // this is super cheap because it mutates VNodes in-place and doesn't change shape
  if (typeof vnode.nodeName==='function' && typeof vnode.nodeName.is==='string') {
    vnode.nodeName = vnode.nodeName.is;
  }
  if (oldHook) oldHook(vnode);
}

@Hotell
Copy link
Author

Hotell commented May 29, 2017

both solutions look good although that one where you check for is may be a weak assumption

@Hotell I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?

checking prototype of the element I guess ?

  if (Object.getPrototypeOf(nodeName) === HTMLElement) {
    return new nodeName();
  }

cc @treshugart

@treshugart
Copy link

I messed with this a bit today and got it working at the diff level while only changing a few lines of code. Problem is, we need the new Babel because the version Preact is currently on doesn't transpile classes correctly to extend natives. Babel is being upgraded in #683 and I have all the code ready to go for this change.

@treshugart
Copy link

💥 #715 💥

@Hotell
Copy link
Author

Hotell commented May 31, 2017

Sweeeeeeet Christmassss !

@treshugart
Copy link

@developit see PR, but re:

I am trying to figure out how Preact could actually resolve this itself - how would it know the given function isn't a Component or pure functional Component?

Basically vnodeName.prototype instanceof HTMLElement in the typeof vnodeName === 'function' check.

@thysultan
Copy link

thysultan commented Jun 1, 2017

Custom Elements also inherit from Node so vnodeName.ELEMENT_NODE === 1 should also work since HTMLElement is not available in the global namespace of ex. Node.js so it might throw when using the renderToString module.

Is HTMLElement the only class Custom Elements can extend?

@treshugart
Copy link

Custom Elements also inherit from Node so vnodeName.ELEMENT_NODE === 1 should also work since HTMLElement is not available in the global namespace of ex. Node.js so it might throw when using the renderToString module.

This would also be a good approach. Happy to explore that option.

Is HTMLElement the only class Custom Elements can extend?

It's the furthest up it can extend. Custom elements can extend other custom elements, though.

@marvinhagemeister
Copy link
Member

Closing as it's not something we're going to do. Since the time this issue was created there have been new jsx functions in jsx-runtime too and I file like the better approach for turning Preact components into custom elements is: https://github.com/preactjs/preact-custom-element .

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.

5 participants