2015-02-09 updates

- [react-server] Support multiple roots | Amjad Masad
- [react-packager] Add a helpful error message when watcher fails to start | Amjad Masad
- [madman] Fix review screen button and animation | Eric Vicenti
This commit is contained in:
Christopher Chedeau
2015-02-10 16:27:57 -08:00
parent d29d07ee06
commit ff2f1ce945
15 changed files with 242 additions and 147 deletions

View File

@@ -88,7 +88,7 @@ class Subscribable {
this._internalEmitter = new EventEmitter();
this._eventMapping = eventMapping || (data => data);
eventEmitter.addListener(
this._upstreamSubscription = eventEmitter.addListener(
eventName,
this._handleEmit,
this
@@ -105,6 +105,13 @@ class Subscribable {
return this._lastData;
}
/**
* Unsubscribe from the upstream EventEmitter
*/
cleanup() {
this._upstreamSubscription && this._upstreamSubscription.remove();
}
/**
* Add a new listener to the subscribable. This should almost never be used
* directly, and instead through Subscribable.Mixin.subscribeTo
@@ -229,6 +236,55 @@ Subscribable.Mixin = {
);
},
/**
* Gets a Subscribable store, scoped to the component, that can be passed to
* children. The component will automatically clean up the subscribable's
* subscription to the eventEmitter when unmounting.
*
* `provideSubscribable` will always return the same Subscribable for any
* particular emitter/eventName combo, so it can be called directly from
* render, and it will never create duplicate Subscribables.
*
* @param {EventEmitter} eventEmitter Emitter to trigger subscription events.
* @param {string} eventName Name of emitted event that triggers subscription
* events.
* @param {function} eventMapping (optional) Function to convert the output
* of the eventEmitter to the subscription output.
* @param {function} getInitData (optional) Async function to grab the initial
* data to publish. Signature `function(successCallback, errorCallback)`.
* The resolved data will be transformed with the eventMapping before it
* gets emitted.
*/
provideSubscribable: function(eventEmitter, eventName, eventMapping, getInitData) {
this._localSubscribables = this._localSubscribables || {};
this._localSubscribables[eventEmitter] =
this._localSubscribables[eventEmitter] || {};
if (!this._localSubscribables[eventEmitter][eventName]) {
this._localSubscribables[eventEmitter][eventName] =
new Subscribable(eventEmitter, eventName, eventMapping, getInitData);
}
return this._localSubscribables[eventEmitter][eventName];
},
/**
* Removes any local Subscribables created with `provideSubscribable`, so the
* component can unmount without leaving any dangling listeners on
* eventEmitters
*/
_cleanupLocalSubscribables: function() {
if (!this._localSubscribables) {
return;
}
var emitterSubscribables;
Object.keys(this._localSubscribables).forEach((eventEmitter) => {
emitterSubscribables = this._localSubscribables[eventEmitter];
Object.keys(emitterSubscribables).forEach((eventName) => {
emitterSubscribables[eventName].cleanup();
});
});
this._localSubscribables = null;
},
componentWillMount: function() {
this._endSubscribableLifespanCallbacks = [];
@@ -241,6 +297,8 @@ Subscribable.Mixin = {
// remaining subscriptions
this._endSubscribableLifespan && this._endSubscribableLifespan();
this._cleanupLocalSubscribables();
// DEPRECATED addListenerOn* usage uses _subscribableSubscriptions array
// instead of lifespan
this._subscribableSubscriptions.forEach(