'use strict';
var ReactUpdates = require('react/lib/ReactUpdates');
var ReactReconciler = require('react/lib/ReactReconciler');
var instantiateReactComponent = require('react/lib/instantiateReactComponent');
var ReactInstanceHandles = require('react/lib/ReactInstanceHandles');
var isEqual = require('lodash.isequal');
var keys = require('lodash.keys');
var map = require('lodash.map');
var defaults = require('lodash.defaults');
var filter = require('lodash.filter');
var pluck = require('lodash.pluck');
var pairs = require('lodash.pairs');
var first = require('lodash.first');
var TreeStream = require('./tree-stream');
var INTERVAL = 8;
var nodeCache = {};
var dirty = {};
var intervals = {};
var streams = {};
var components = {};
var getId = function getId(node) {
return node.id;
};
var getNode = function getNode(id) {
return nodeCache[id];
};
var getChildrenIds = function getChildrenIds(id) {
return pluck(filter(map(keys(nodeCache), function (id) {
return getNode(id);
}), function (node) {
return node.parent === id;
}), 'id');
};
var setNode = function setNode(id, node) {
if (isEqual(nodeCache[id], node)) {
return node;
}
ReactInstanceHandles.traverseAncestors(id, function (id) {
dirty[id] = true;
return false;
});
nodeCache[id] = node;
return node;
};
var purgeId = function purgeId(id) {
map(getChildrenIds(id), purgeId);
delete nodeCache[id];
delete components[id];
delete streams[id];
};
var buildTree = function buildTree(id) {
var node = getNode(id);
var children = getChildrenIds(id);
return defaults({
children: map(children, buildTree)
}, node);
};
var startLoop = function startLoop(id) {
if (intervals[id]) {
return;
}
if (!keys(streams).length) {
delete dirty[id];
return;
}
intervals[id] = setTimeout(function () {
tick(id);
}, INTERVAL);
};
var tick = function tick(id) {
delete intervals[id];
var isDirty = function isDirty() {
dirty[id] = false;
if (!streams[id]) {
return;
}
streams[id].writeTree(buildTree(id));
};
if (dirty[id]) {
isDirty();
}
startLoop(id);
};
var getNodeInContainer = function getNodeInContainer(stream) {
return map(filter(pairs(streams), function (kv) {
return kv[1] === stream;
}), function (kv) {
return getNode(kv[0]);
});
};
var getRootIds = function getRootIds(stream) {
return map(getNodeInContainer(stream), function (node) {
return getId(node);
});
};
var register = function register(stream, component) {
var rootId = first(getRootIds(stream));
if (rootId) {
rootId = ReactInstanceHandles.getReactRootIDFromNodeID(rootId);
}
if (!rootId) {
rootId = ReactInstanceHandles.createReactRootID();
}
streams[rootId] = stream;
components[rootId] = component;
return rootId;
};
var mountComponent = function mountComponent(component, rootId, stream, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(function () {
var node = ReactReconciler.mountComponent(component, rootId, transaction, context);
dirty[node.id] = true;
startLoop(node.id);
});
ReactUpdates.ReactReconcileTransaction.release(transaction);
};
var unmountComponent = function unmountComponent(rootId, component) {
ReactReconciler.unmountComponent(component);
purgeId(rootId);
};
var ReactMount = {
render: function render(element) {
var stream = new TreeStream();
var component = instantiateReactComponent(element);
var rootId = register(stream, component);
ReactUpdates.batchedUpdates(mountComponent, component, rootId, stream, false);
return stream;
},
unmountComponentAtNode: function unmountComponentAtNode(stream) {
var rootId = first(getRootIds(stream));
var component = components[rootId];
if (!component) {
return false;
}
ReactUpdates.batchedUpdates(unmountComponent, rootId, component, stream);
stream.writeTree(null);
return true;
},
getID: getId,
getNode: getNode,
setNode: setNode,
purgeId: purgeId
};
module.exports = ReactMount; |