Skip to content

Instantly share code, notes, and snippets.

@mikaelbr
Last active February 21, 2017 23:50
Show Gist options
  • Save mikaelbr/9dfac0137f5baf294863 to your computer and use it in GitHub Desktop.
Save mikaelbr/9dfac0137f5baf294863 to your computer and use it in GitHub Desktop.
Example of functional UI and higher order components for React/Omniscient.js (http://omniscientjs.github.io/)
import React from 'react';
import component from 'omniscient';
import immstruct from 'immstruct';
import { compose, valueNode, partialProps } from './compose';
const data = immstruct({ counter: 0, title: 'My title' });
const em = component(({partialedTitle, children}) =>
<em>{partialedTitle}: {children}</em>);
const h1 = component(({titleCursor, children}) =>
<h1 className="heading--someModifier">{titleCursor.valueOf()}: {children}</h1>);
const partialedEm = partialProps(em, {
partialedTitle: 'My partialed property'
});
const ComposedComponent = compose(h1, partialedEm, valueNode(props => props.counter));
const App = component(function (props) {
return <div>
<h1>Composed on valueNode</h1>
{ComposedComponent(props)}
</div>
});
const render = () =>
React.render(
App({
counter: data.cursor('counter'),
titleCursor: data.cursor('title')
}),
document.body);
/* or simply:
const render = () =>
React.render(
ComposedComponent({
counter: data.cursor('counter'),
titleCursor: data.cursor('title')
}),
document.body);
*/
render();
data.on('swap', render);
setInterval(
() => data.cursor().update('counter', i => i + 1),
1000);
import React from 'react';
import assign from 'lodash.assign';
export function compose (...fns) {
return (...args) =>
fns.reduceRight((child, fn) =>
fn.apply(this, child ? args.concat(child) : args), null);
};
// For just using single value nodes in React.
export function valueNode (pick) {
return props => <span>{pick(props).valueOf()}</span>;
};
//-- Partial Applications
export function partialProps (fn, partialInput) {
return function partialedProps (key, props, statics) {
// Same default args code as Omniscient Core
let _props;
if (typeof key === 'object') {
statics = props;
props = key;
key = void 0;
}
if (isCursor(props) || isImmutable(props)) {
_props = props;
} else {
_props = assign({}, props, partialInput);
}
const children = flatten(sliceFrom(arguments, statics).filter(isNode));
if (isNode(statics)) {
statics = void 0;
}
return fn.apply(this, [key, _props, statics].concat(children));
};
}
// * Helpers for partial implementation. Functions from Omniscient core
function toArray (args) {
return [].slice.apply(args);
}
function isCursor (potential) {
return !!(potential && typeof potential.deref === 'function');
}
var IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@';
function isImmutable(maybeImmutable) {
return !!(maybeImmutable && maybeImmutable[IS_ITERABLE_SENTINEL]);
}
function flatten (array) {
return Array.prototype.concat.apply([], array);
}
function sliceFrom (args, value) {
var array = toArray(args);
var index = Math.max(array.indexOf(value), 0);
return array.slice(index);
}
function isNode (propValue) {
switch (typeof propValue) {
case 'number':
case 'string':
return true;
case 'boolean':
return !propValue;
case 'object':
if (Array.isArray(propValue)) {
return propValue.every(isNode);
}
if (React.isValidElement(propValue)) {
return true;
}
return false;
default:
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment