choojs / nanohtml

:dragon: HTML template strings for the Browser with support for Server Side Rendering in Node.
MIT License
687 stars 49 forks source link

Using JSX with Bel #56

Open arturi opened 8 years ago

arturi commented 8 years ago

I was wondering if its possible to use JSX instead of hyperx to create elements with Bel. I like to use Bel and Yo-yo, but sometimes its convenient to go JSX route, because then components could be re-used between Yo-yo/Choo and React projects.

I tried to set jsx pragma to h and then use it like so:

var h = require('bel').createElement

var el = <h1 class="a">Hi</h1>
document.body.appendChild(el)

But children (Hi) never appear, and if you omit class, bel complaints: Cannot read property 'namespace' of null. Got confused, probably missing something.

nichoth commented 8 years ago

Sometimes I have wished that all the h modules were more interoperable. They all have slight differences that make them incompatible. bel, virtual-hyperscript, hyperscript, and react all have slight differences, though they do basically the same thing.

shama commented 8 years ago

What does the compiled JSX code look like?

arturi commented 8 years ago

Compiled JSX with createElement as pragma looks like this:

const el = createElement(
  'h1',
  null,
  'Hi!'
)

And Bel, according to the docs, expects it to be:

var sameElement = createElement('h1', { className: 'heading' }, ['Hi!'])

More complex nested example:

const el = <div>
  <h1>Hi, <strong>stranger</strong></h1>
  <h2>Hello</h2>
</div>

Transpiled:

const el = createElement(
  'div',
  null,
  createElement(
    'h1',
    null,
    'Hi, ',
    createElement(
      'strong',
      null,
      'stranger'
    )
  ),
  createElement(
    'h2',
    null,
    'Hello'
  )
);
shama commented 8 years ago

So their spec expects createElement(tag, attrs, ...children)? I'm +1 on support their spec but low priority on my list.

arturi commented 8 years ago

So you’d want to support both or switch to their?

shama commented 8 years ago

Support both if possible, not switch to theirs.

yoshuawuyts commented 8 years ago

Hmmm, I feel the solution for bel / react compat would probably be to create a form of generic adapter that allows for arbitrary nodes to be passed into react, and just work™ - similar to how we've done it with cache-element/widget but for React

Does that make sense?

On Sun, Oct 23, 2016 at 5:25 AM nichoth notifications@github.com wrote:

Sometimes I have wished that all the h modules were more interoperable. They all have slight differences that make them incompatible. bel, virtual-hyperscript, hyperscript, and react all have slight differences, though they do basically the same thing.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/shama/bel/issues/56#issuecomment-255567353, or mute the thread https://github.com/notifications/unsubscribe-auth/ACWlel4YFRXU1W3zFpjh5QOPGtl_69-hks5q2tOZgaJpZM4KeCR3 .

yoshuawuyts commented 8 years ago

seems it took a while for my emails to arrive 😅

On Tue, Oct 25, 2016 at 8:42 PM Yoshua Wuyts notifications@github.com wrote:

Hmmm, I feel the solution for bel / react compat would probably be to create a form of generic adapter that allows for arbitrary nodes to be passed into react, and just work™ - similar to how we've done it with cache-element/widget but for React

Does that make sense?

On Sun, Oct 23, 2016 at 5:25 AM nichoth notifications@github.com wrote:

Sometimes I have wished that all the h modules were more interoperable. They all have slight differences that make them incompatible. bel, virtual-hyperscript, hyperscript, and react all have slight differences, though they do basically the same thing.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/shama/bel/issues/56#issuecomment-255567353, or mute the thread < https://github.com/notifications/unsubscribe-auth/ACWlel4YFRXU1W3zFpjh5QOPGtl_69-hks5q2tOZgaJpZM4KeCR3

.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/shama/bel/issues/56#issuecomment-256133936, or mute the thread https://github.com/notifications/unsubscribe-auth/ACWlehKji7daqf9h4aF59xpz913D_jmhks5q3k16gaJpZM4KeCR3 .

tunnckoCore commented 7 years ago

One more thing related to this "spec" compliance is because to support components, not tags which means passing a functions as first argument not a string

var el = <Link foo="bar">Hello</Link>

is transpiled to

// should also support content to be a string, not an array
// because currently isn't possible
var el = createElement(Link, { foo: 'bar' }, ['Hello'])
tunnckoCore commented 7 years ago

kinda such thing

function arrayify (val) {
  if (!val) return [];
  if (Array.isArray(val)) return val;
  return [val];
}

function isObject (val) {
  return val && typeof val === 'object' && !Array.isArray(val)
}

function belCreateElement (tag, props, children) {
  var el

  children = arrayify(children)
  props = isObject(props) ? props : {}

  if (typeof tag === 'function') {
    // not sure for the order
    // but we should have `children` on `props` object
    props.children = children || arrayify(props.children)
    var _ele = tag(props, props.children)

    // should remove `_ele._attributes.null.children`
    return _ele
  }

  // not changed code below ...
}

seems to be working

examples

function Link (props) {
  return belCreateElement('a', props, props.children.concat(' world!!'))
}

var el = belCreateElement(Link, { className: 'foo' }, 'Hello')

console.log(el.toString())

Ahref, not using props argument directly (important to test both above and below cases!)

function Ahref ({ className, children }) {
  return belCreateElement('a', { className }, children.concat(' world!!'))
}

var el = belCreateElement(Ahref, { className: 'foo' }, 'Hello')

console.log(el.toString())

MOAR

var hx = require('hyperx')(belCreateElement)

function Link (props) {
  // JSX
  // <a { ...props }>{ props.children.concat(' world...').join('') }</a>
  return hx`<a ${ /* ...props */ } >${ props.children.concat(' world...').join('') }</a>`
}

// JSX
// var el = <Link className="foobar">Hello</Link>
var el = belCreateElement(Link, { className: 'foobar' }, 'Hello')

console.log(el.toString())
// expected
// => '<a class="foobar">Hello world...</a>'