ReactCrate

Trying a new way to compose Components

by Kye Hohenberger

HTML

<script src="https://unpkg.com/preact/dist/preact.min.js"></script>
<script src="https://unpkg.com/[email protected]"></script>
<div id="o"></div>

CSS

html,
body,
#app,
.full-size {
  font-family: sans-serif;
  width: 100%;
  margin: 0;
}

.child-c.list {
  list-style: none;
  padding: 4px;
  border: 2px solid green;
}

Babel + JSX

const {
  h,
  render
} = preact;
/** @jsx h */
const classnames = window.classNames
console.clear();
const el = document.getElementById('o');

const isFn = test => typeof test === 'function'
const id = Component => Component

class Crate {
  constructor (hoc = id) {
    this.hocFn = hoc
    return this
  }

  map (fn) {
    return new Crate(fn(this.hocFn))
  }

  fold (fn) {
    return fn(this.hocFn)
  }

  compile (Component) {
    return this.fold(hoc => hoc(Component))
  }

  hoc (fn) {
    return this.map(hoc => Wrapped => fn(hoc(Wrapped)))
  }

  // Inspect your props at a certain point
  inspect () {
    return this.map(hoc => Wrapped => hoc(props => {
      console.log('=== inspect ===')
      console.log('props', props)
      console.log('this.hocFn', this.hocFn)
      console.log('===============')
      return h(Wrapped, props)
    }))
  }

  // Set prop by key
  prop (key, value) {
    return this.map(hoc => Wrapped => hoc(props => {
      const nextProps = { ...props, [key]: isFn(value) ? value(props) : value }
      return h(Wrapped, nextProps)
    }))
  }

  // Set className prop
  // accepts anything that `classnames`
  // https://www.npmjs.com/package/classnames
  className (value) {
    return this.prop('className', props => {
      return classnames(props.className, isFn(value) ? value(props) : value)
    })
  }

  // Set style
  style (s) {
    return this.prop('style', props => ({
      ...props.style,
      ...(isFn(s) ? s(props) : s)
    }))
  }
}

function crate (...args) {
  return new Crate(...args)
}

const pp = obj => JSON.stringify(obj, null, 2)

function Stateless (props) {
  return (
    <pre style={props.style} className={props.className}>
      <details open>
        <summary>Stateless Component Props</summary>
        {pp(props)}
      </details>
      {props.children}
    </pre>
  )
}

const buttonCrate = crate().style({
  height: '1.6em',
  width: '100%',
  background: 'none',
  color: 'white',
  fontSize: '1em',
  outline:...