Skip to content

Instantly share code, notes, and snippets.

@jmakeig
Last active May 29, 2019 06:35
Show Gist options
  • Save jmakeig/2621f2153c20086bcc14dfd564c14629 to your computer and use it in GitHub Desktop.
Save jmakeig/2621f2153c20086bcc14dfd564c14629 to your computer and use it in GitHub Desktop.

JSX is not JavaScript. It introduces new syntax to JavaScript as sugar for React’s underlying JavaScript APIs. JSX is problematic—to me, at least—for several reasons. It requires,

  • An extra compilation step
  • Specialized support in your IDE
  • Some mind bending in the fuzzy areas that aren’t quite JavaScript and aren’t quite HTML, for example, the awful <> tag
  • Lock-in to Facebook’s view of the web

The creatively named react-helper library, provides a humane API for creating DOM elements using React that abstracts away React altogether. It’s possible to interoperate with React components, but for pure DOM manipulation, the API does not leak the underlying React implementation.

p(
  'Parameters ', 
  span({style: fontStyle: 'italic', 'can go', { style: fontWeight: 'bold' }), 
  { onClick: (evt) => {/**/} }, 
  [' in ', ' any ', ' order.']
);
import React from 'react';
import ReactDOM from 'react-dom';
/**
* Whether something is not `undefined` or `null`
*
* @param {*} item
* @return {boolean}
*/
function exists(item) {
return !('undefined' === typeof item || null === item);
}
function isPrimitive(obj) {
switch (typeof obj) {
case 'string':
case 'number':
case 'boolean':
case 'undefined':
return true;
case 'object':
return null === obj;
}
return false;
}
function isIterable(item, ignoreStrings = true) {
if (!exists(item)) return false;
if ('function' === typeof item[Symbol.iterator]) {
return 'string' !== typeof item || !ignoreStrings;
}
return false;
}
function isReact(obj) {
return (
obj instanceof React.Component ||
Symbol.for('react.element') === obj['$$typeof']
);
}
/**
*
* @param {String|React.Component} name
* @param {...any} stuff Any combination of properties object or React.Component
* Iterables will be flattened and applied recusively
* @return {React.DetailedReactHTMLElement} Same as `React.createElement`
*/
function el(name, ...stuff) {
const props = {};
const children = [];
function dispatch(thing) {
if (isReact(thing) || isPrimitive(thing)) {
children.push(thing);
} else if ('function' === typeof thing) {
// Doesn’t allow for passing props. Is this right?
children.push(React.createElement(thing));
} else if (isIterable(thing)) {
for (const item of thing) {
dispatch(item);
}
} else {
for (let p in thing) {
switch (p) {
case 'style':
case 'dataset':
props[p] = Object.assign({}, props[p], thing[p]);
break;
default:
props[p] = thing[p];
}
}
}
}
for (const thing of stuff) {
dispatch(thing);
}
return React.createElement(name, props, ...children);
}
export function renderInto(component, element) {
return ReactDOM.render(component, element);
}
/* prettier-ignore */ export const toFragment = (...p) => el(React.Fragment, ...p);
/* prettier-ignore */ export const empty = () => toFragment();
/* prettier-ignore */ export const header = (...p) => el('header', ...p);
/* prettier-ignore */ export const nav = (...p) => el('nav', ...p);
/* prettier-ignore */ export const footer = (...p) => el('footer', ...p);
/* prettier-ignore */ export const div = (...p) => el('div', ...p);
/* prettier-ignore */ export const p = (...p) => el('p', ...p);
/* prettier-ignore */ export const h1 = (...p) => el('h1', ...p);
/* prettier-ignore */ export const h2 = (...p) => el('h2', ...p);
/* prettier-ignore */ export const h3 = (...p) => el('h3', ...p);
/* prettier-ignore */ export const h4 = (...p) => el('h4', ...p);
/* prettier-ignore */ export const h5 = (...p) => el('h5', ...p);
/* prettier-ignore */ export const h6 = (...p) => el('h6', ...p);
/* prettier-ignore */ export const ul = (...p) => el('ul', ...p);
/* prettier-ignore */ export const ol = (...p) => el('ol', ...p);
/* prettier-ignore */ export const li = (...p) => el('li', ...p);
/* prettier-ignore */ export const dl = (...p) => el('dl', ...p);
/* prettier-ignore */ export const dt = (...p) => el('dt', ...p);
/* prettier-ignore */ export const dd = (...p) => el('dd', ...p);
/* prettier-ignore */ export const table = (...p) => el('table', ...p);
/* prettier-ignore */ export const thead = (...p) => el('thead', ...p);
/* prettier-ignore */ export const tfoot = (...p) => el('tfoot', ...p);
/* prettier-ignore */ export const tbody = (...p) => el('tbody', ...p);
/* prettier-ignore */ export const tr = (...p) => el('tr', ...p);
/* prettier-ignore */ export const th = (...p) => el('th', ...p);
/* prettier-ignore */ export const td = (...p) => el('td', ...p);
/* prettier-ignore */ export const span = (...p) => el('span', ...p);
/* prettier-ignore */ export const a = (...p) => el('a', ...p);
/* prettier-ignore */ export const em = (...p) => el('em', ...p);
/* prettier-ignore */ export const strong = (...p) => el('strong', ...p);
/* prettier-ignore */ export const mark = (...p) => el('mark', ...p);
/* prettier-ignore */ export const input = (...p) => el('input', { type: 'text' }, ...p);
/* prettier-ignore */ export const button = (...p) => el('button', ...p);
/* prettier-ignore */ export const text = input;
/* prettier-ignore */ export const textarea = (...p) => el('textarea', ...p);
/* prettier-ignore */ export const checkbox = (...p) => el('input', { type: 'checkbox' }, ...p);
/* prettier-ignore */ export const radio = (...p) => el('input', { type: 'radio' }, ...p);
/* prettier-ignore */ export const select = (...p) => el('select', ...p);
/* prettier-ignore */ export const option = (...p) => el('option', ...p);
/* prettier-ignore */ export const file = (...p) => el('input', { type: 'file' }, ...p);
/* prettier-ignore */ export const br = (...p) => el('br', ...p);
/* prettier-ignore */ export const hr = (...p) => el('hr', ...p);
@skliffmueller
Copy link

I agree with your bullet points. And what I'm about to say, I don't have any strong bias for Angular or React. I feel one thing Angular got right, is they kept html the way it is, and just expanded on it's attribute interfaces. Downfall of Angulars approach is not having the ability to wrap all of the javascript methods available on the same page, it requires building a function into your component to provide to the Angular template. Strong point of React is being able to include your javascript into the JSX template in a pseudo like html approach. Downfall of React, is it gives new developers opportunities to make big mistakes shoving too much javascript into their render, and isn't too friendly to some designers who like to get into modifying HTML and CSS.

What you made is an interesting approach to a template engine, I can see this working well along side a theme framework like material-ui, or building some sort of React CMS. I can already see a pretty easy translation of a JSON object of nested elements into a content page React can understand. Or building an extension against acorn for parsing/unit testing.

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