Skip to content

Instantly share code, notes, and snippets.

@developit
Last active June 27, 2020 14:49
Show Gist options
  • Save developit/da77a4d3bbf365908c8c to your computer and use it in GitHub Desktop.
Save developit/da77a4d3bbf365908c8c to your computer and use it in GitHub Desktop.
WTF IS JSX
/** Render Virtual DOM to the real DOM */
function render(vnode) {
if (typeof vnode==='string') return document.createTextNode(vnode);
let n = document.createElement(vnode.nodeName);
Object.keys(vnode.attributes || {}).forEach( k => n.setAttribute(k, vnode.attributes[k]) );
(vnode.children || []).forEach( c => n.appendChild(render(c)) );
return n;
}
/** hyperscript generator, gets called by transpiled JSX */
function h(nodeName, attributes, ...args) {
let children = args.length ? [].concat(...args) : null;
return { nodeName, attributes, children };
}
/** @jsx h */
// ^^^^ this tells a transpiler to inject calls to an `h()` function for each node.
const ITEMS = 'hello there people'.split(' ');
// a "partial" that does a filtered loop - no template BS, just functional programming:
function foo(items) {
// imagine templates that adhere to your JS styleguide...
return items.map( p => <li> {p} </li> ); // <-- can be multiline
}
// a simple JSX "view" with a call out ("partial") to generate a list from an Array:
let vdom = (
<div id="foo">
<p>Look, a simple JSX DOM renderer!</p>
<ul>{ foo(ITEMS) }</ul>
</div>
);
// render() converts our "virtual DOM" (see below) to a real DOM tree:
let dom = render(vdom);
// append the new nodes somewhere:
document.body.appendChild(dom);
// Remember that "virtual DOM"? It's just JSON - each "VNode" is an object with 3 properties.
let json = JSON.stringify(vdom, null, ' ');
// The whole process (JSX -> VDOM -> DOM) in one step:
document.body.appendChild(
render( <pre>{ json }</pre> )
);

Moved to my new blog: jasonformat.com/wtf-is-jsx

It's actually really straightforward. Take 1 minute and read this.

Pragma

You declare this per-file or per-function to tell your transpiler (eg: Babel) the name of a function that should be called (at runtime) for each node.

In the example below, we are saying "inject calls to an h() function for each node":

/** @jsx h */

Transpilation

Before:

/** @jsx h */
let foo = <div id="foo">Hello!</div>;

After:

var foo = h('div', {id:"foo"}, 'Hello!');

That's all there is going on here. It's just a sugar for a syntax that's already pretty decent.


Let's Build a JSX Renderer

First, we'll need to define that h() function our transpiled code is calling:

function h(nodeName, attributes, ...args) {
	let children = args.length ? [].concat(...args) : null;
	return { nodeName, attributes, children };
}

Ok, that was easy. The ...args bit is for multiple child nodes.

Now we have these nested JSON objects our h() function spits out, so we end up with a "tree" like this:

{
  nodeName: "div",
  attributes: {
    "id": "foo"
  },
  children: ["Hello!"]
}

So we just need a function that accepts that format and spits out actual DOM nodes:

function render(vnode) {
	if (typeof vnode==='string') return document.createTextNode(vnode);
	let n = document.createElement(vnode.nodeName);
	Object.keys(vnode.attributes || {}).forEach( k => n.setAttribute(k, vnode.attributes[k]) );
	(vnode.children || []).forEach( c => n.appendChild(render(c)) );
	return n;
}

Sweet.


Usage

const ITEMS = 'hello there people'.split(' ');
 
// a "partial" that does a filtered loop - no template BS, just functional programming:
function foo(items) {
	// imagine templates that adhere to your JS styleguide...
	return items.map( p => <li> {p} </li> );		// <-- can be multiline
}
 
// a simple JSX "view" with a call out ("partial") to generate a list from an Array:
let vdom = (
	<div id="foo">
		<p>Look, a simple JSX DOM renderer!</p>
		<ul>{ foo(ITEMS) }</ul>
	</div>
);
 
// render() converts our "virtual DOM" (see below) to a real DOM tree:
let dom = render(vdom);
 
// append the new nodes somewhere:
document.body.appendChild(dom);
 
// Remember that "virtual DOM"? It's just JSON - each "VNode" is an object with 3 properties.
let json = JSON.stringify(vdom, null, '  ');
 
// The whole process (JSX -> VDOM -> DOM) in one step:
document.body.appendChild(
	render( <pre>{ json }</pre> )
);

Codepen by developit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment