Skip to content

Instantly share code, notes, and snippets.

@mikaelbr
Last active November 2, 2015 00:01
Show Gist options
  • Save mikaelbr/48d483bb7d078cf34cb8 to your computer and use it in GitHub Desktop.
Save mikaelbr/48d483bb7d078cf34cb8 to your computer and use it in GitHub Desktop.
(Experimental) Derivate coupled components instead of creating non reusables. See running demo here: http://goo.gl/LgF5mz
var data = immstruct({
message: 'Hello, World!'
});
// Reference cursors is kind of a compromise and is a result of this:
// https://github.com/omniscientjs/omniscient/issues/42 (sorry, alot of text)
// One of the problem I have with reference cursors is that
// by using "reference cursors" directly in a component, we bind
// that component to the specific part of the structure and it hurts
// re-usability.
// Instead of binding this "original" component to the "reference cursor"
// We derive a new component from it which is not as re-usable as the
// original. See `CoupledComponent`
const GeneralComponent = component(referenceCursorUpdate(), ({reference}) => {
return <h1>Message: {reference.cursor().valueOf()}</h1>
});
// This derived component would be bound to the reference, but
// leave the original in tact.
var myReference = data.reference('message');
const CoupledComponent = partialProps(GeneralComponent, {
reference: myReference
});
// Now, updating the part of the structure the reference points
// to, would re-render the component.
setTimeout(_ =>
myReference.cursor().update(_ => 'Bye, World!'), 1000);
render();
/*
function render () {
// could pass in as well, but that might defeat the purpose of references
React.render(
GeneralComponent({ reference: data.reference('message')}),
el);
}
*/
function render () {
React.render(CoupledComponent(), el);
}
// Used as mixin to update the component directly when
// the cursor the "reference" points at updates.
function referenceCursorUpdate () {
var cancel;
return {
componentDidMount: function () {
if (!this.props.reference) return;
cancel = this.props.reference.observe(_ => this.forceUpdate());
},
componentWillUnmount: function () {
cancel();
}
};
}
// Implementation of the Partial function
function assign (target, firstSource) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert first argument to object');
}
var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource === undefined || nextSource === null) {
continue;
}
var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
to[nextKey] = nextSource[nextKey];
}
}
}
return to;
}
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