Skip to content

Instantly share code, notes, and snippets.

@andrewluetgers
Last active August 29, 2015 14:17
Show Gist options
  • Save andrewluetgers/7768f74f9b2f2ae8730a to your computer and use it in GitHub Desktop.
Save andrewluetgers/7768f74f9b2f2ae8730a to your computer and use it in GitHub Desktop.
Omniscient / Immstruct - forceUpdateOnState
/**
* forceUpdateOnState
*
* @param state immstruct structure
* @returns component mixins {componentWillMount, componentWillUpdate, componentWillUnmount}
*
* the parent component should provide to the target component
* a map of prop names as keys for cursors and period delimited path strings
* on as 'cursors' property, e.g. <div cursors={{foo: 'bar.foo'}}></div>
* in the above example: props.foo = state.reference(['bar', 'foo']).cursor()
* updates will not be top-down component will watch the path ref and
* force update upon any change
*/
function forceUpdateOnState(state) {
return {
componentWillMount: function() {
var component = this;
component._cancel = [];
// create references, force update on change, attach cursors
component.references = getReferences(state, component);
var refs = component.references;
for (var name in refs) {
// attach cursor to instance
var ref = refs[name];
component[name] = ref.cursor();
// force update on change
// store cleanup function unmount
component._cancel.push(ref.observe('swap', function() {
component.forceUpdate();
}));
}
},
componentWillUpdate: function() {
// update cursors
var component = this,
refs = component.references;
for (var name in refs) {
component[name] = refs[name].cursor();
}
},
componentWillUnmount: function() {
// cancel observers
this._cancel.forEach(function(fn) {fn()});
}
};
};
/**
* getReferences
* expects either a member or prop of 'cursors' such as {foo: 'foo', baz: 'bar.baz'}
* where the key is the name of a new member to add to the instance and
* the value is a space delimited path within provided state
* @param state immstruct structure
* @param component omniscient or react component instance
* @returns {someName: state.reference(['some', 'path']), ...}
*/
var references = {};
function getReferences(state, component) {
var sources = {
// support component defining own cursors
componentDefs: component.cursors,
// support parent defining cursors in props, will override prior cursors
propDefs: (component.props && component.props.cursors)
},
refs = {};
for (var source in sources) {
var cursorDefs = sources[source];
for (var name in cursorDefs) {
var selector = cursorDefs[name];
// in conjunction with forceUpdateOnState will attache references and cursors to the instance
// it will also register reference observers that will force render and refresh cursors upon change
references[selector] = refs[name] = references[selector] || state.reference(selector.split("."));
}
}
return refs;
}
module.exports = forceUpdateOnState;
// basic usage of forceUpdateOnState
var React = require('react'),
component = require('omniscient'),
immstruct = require('immstruct')
forceUpdateOnState = require("./forceUpdateOnState");
var state = immstruct({query: {term: 'test', size: 100}});
var mixins = forceUpdateOnState(state);
var Thing1 = component("Thing1", [{
// can define cursors within the component
// this will create a cursor and put it on this.term
cursors: {
term: 'query.term'
}
}, mixins], function() {
// can also define from parent level (does not support both)
// do not pass parent's cursors down to children
// insted define them via the cursors object
return (
<div>
{this.term.deref()}
<Thing2 cursors={{size: 'query.size'}} />
</div>
);
});
var Thing2 = component("Thing2", [mixins], function() {
return (
<div>this.size.deref()</div
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment