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

@hyperapp/html: use a Proxy? #1087

Open
mindplay-dk opened this issue Feb 23, 2022 · 9 comments
Open

@hyperapp/html: use a Proxy? #1087

mindplay-dk opened this issue Feb 23, 2022 · 9 comments
Labels
enhancement New feature or request

Comments

@mindplay-dk
Copy link

In modern browsers, can we use a Proxy instead of hard coding all the element types?

I'm using this right now:

    import { h } from "./lib/hyperapp.js";

    const { main, h1, input, ul, li, button } = new Proxy(h, {
      get(h, type) { return (props, children) => h(type, props, children) }
    });

One more line of code, and you have support for custom elements:

    const { hyperApp } = new Proxy(h, {
      get(h, type) {
        const name = type.replace(/([A-Z])/g, c => "-" + c.toLowerCase());
        return (props, children) => h(name, props, children)
      }
    });

    console.log(hyperApp({foo:"bar"}, ["baz"])); // type: "hyper-app"

Thoughts? :-)

@jorgebucaran jorgebucaran added the enhancement New feature or request label Feb 23, 2022
@icylace
Copy link
Contributor

icylace commented Feb 25, 2022

In the past I've used a similar Proxy technique for creating element functions on-the-fly. It certainly feels cleaner. Your one-liner for custom elements support is pretty neat !

However, if I'm not mistaken using a Proxy comes at the cost of some performance, which to be fair is probably fine for a lot of cases.

My preference nowadays is to use h directly, though.

@mindplay-dk
Copy link
Author

However, if I'm not mistaken using a Proxy comes at the cost of some performance, which to be fair is probably fine for a lot of cases.

In this case, the Proxy is just a factory for the element creation functions - there is no run-time performance overhead once you've generated your functions.

My only reservation is this only works in modern browsers and Proxy can't be polyfilled.

@icylace
Copy link
Contributor

icylace commented Feb 25, 2022

It's good to know the Proxy performance penalty is not a concern. That said, it looks like the primary advantage of this technique over the current technique is the custom elements support. I think @hyperapp/html is tree-shakeable so code size for production shouldn't be a big difference in most cases. Though, maintaining a big clunky list of exported functions is annoying but I think that's a small price to pay to not have to worry about browser support.

I actually would like it if Proxy was used but like you said, it only works where it works.

@zaceno
Copy link
Contributor

zaceno commented Mar 4, 2022

Unless you need IE11 support (which I don't think hyperapp supports anyway) Proxy is fine! I've used the approach suggested here in the past as well and really it's fine :)

The very small issues I've had with it are:

  • Tree shaking (but now that I'm striving to be bundler-free, proxy is technically less to import)
  • I didn't manage to declare types for the proxy. But that doesn't mean there isn't a way to do it ;)
  • I prefer the one-liner: `import {div, p, text} from '@hyperapp/html' rather than the two-liner:
import html from '@hyperapp/html'
const {div, p, text} = html

Those aren't huge issues though and I could go either way. 🤷 😄

@jorgebucaran
Copy link
Owner

Custom elements is a nice plus. Now, the purpose here would be shipping less code with the package or would there be anything else?

I prefer the one-liner: import {div, p, text} from '@hyperapp/html' rather than the two-liner.

In the land of the multi-line, the one-liner is king.

@mindplay-dk
Copy link
Author

One-liner is probably doable with export = ?

@sergey-shpak
Copy link
Contributor

pf, using Proxy since early beginning (looks like already more than 2 years):
https://gist.github.com/sergey-shpak/9266316d9abd550ddbeac2c88e5a0d22

@mindplay-dk
Copy link
Author

@sergey-shpak great minds... heh. I too tried to get around the noisy empty attributes object you have to pass with every call - although, for some reason, my attempts seemed to break the official examples... children aren't always an array, I think?

Such a small thing, but it would make the examples look much better. I guess it's a breaking change though...

@sergey-shpak
Copy link
Contributor

sergey-shpak commented Mar 22, 2022

@mindplay-dk the gist is pretty old I haven't updated it for a while, my current implementation is following:

import {h, text} from 'hyperapp'

/* Html factory */
export const html = new Proxy({}, {
  get: (target, name) => name !== 'text'
  ? (attrs = {}, child) => 
    !Array.isArray(attrs) && !attrs.tag
    ? h(name, attrs, child)
    : h(name, {}, attrs)
  : text
})

and the usage

const {div, span, text} = html 

// no useless array for single element
// and no empty props object
div(text('Some text'))

// or something like
div([
  span(text('some text')),
  span(text('other text'))
])

// and common usage
div({class: 'active'}, text('no array for single child'))

*added later: works with [email protected], I haven't tested with other versions but should be ok

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

No branches or pull requests

5 participants