Last active
November 2, 2015 00:01
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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