mowgli
Version:
Thin layer over React JS for interacting with a single tree of state
81 lines (61 loc) • 2.67 kB
JavaScript
;
import React from 'react';
import {getCursorFns, reducePaths, mapObj, throwError, debounce} from 'junglejs-common';
// know if we are on the client or the server
const isBrowser = !(global && Object.prototype.toString.call(global.process) === '[object process]');
let cursorFns;
export default {
contextTypes: {
tree: React.PropTypes.object,
actions: React.PropTypes.object,
cursorFns: React.PropTypes.object
},
componentWillMount: function() {
const tree = this.context && this.context.tree;
if (!tree) throwError(`No tree has been passed to your root component`);
const actions = this.context && this.context.actions;
const cursorDefs = this.data;
const actionDefs = this.actions;
cursorFns = getCursorFns(tree);
// add the declared actions to the component
this.actions = getActions(actionDefs, actions);
// get a map of initial cursors pointing to subsets of the tree
const cursors = getCursors(cursorDefs, tree);
// get an array of subscriptions to be applied
this._subscriptions = getSubscriptions(this, cursors);
// subscribe to the update event for each cursor
const subscribe = s => cursorFns.on(s.cursor, s.subscribe);
this._subscriptions.forEach(subscribe);
// add the cursor values to the component state
const cursorValues = getCursorValues(cursors);
this.setState(cursorValues);
},
componentWillReceiveProps: function() {
const tree = this.context = this.context.tree;
const cursorDefs = this.data;
// update cursor values
const cursorValues = getCursorValues(getCursors(cursorDefs, tree));
this.setState(cursorValues);
},
componentWillUnmount: function() {
// unsubscribe from all changes to the tree
const unsubscribe = s => cursorFns.off && cursorFns.off(s.cursor, s.subscribe);
this._subscriptions.forEach(unsubscribe);
}
};
// get a map of cursors pointing to subsets of the tree
const getCursors = (cursorDefs, tree) => reducePaths(cursorDefs, tree, cursorFns.get);
// get a map of cursor values
const getCursorValues = cursors => mapObj(cursors, cursor => cursorFns.value(cursor));
// get a map of actions pointing to actions in the context
const getActions = (actionDefs, actions) => reducePaths(actionDefs, actions, false, 'Action');
// get an array of all subscriptions to apply
const getSubscriptions = (component, cursors) => {
// render from root on the server
if (!isBrowser || !cursorFns.on) return [];
// return an array of subscription functions that update tree on change
return Object.keys(cursors).map(key => ({
cursor: cursors[key],
subscribe: debounce(() => component.setState({[key]: cursorFns.value(cursors[key])}))
}));
};