mirror of
https://github.com/status-im/react-native.git
synced 2025-01-09 09:12:02 +00:00
516bf7bd94
Summary: The `EmitterSubscription.remove()` method was previously calling `this.subscriber.removeSubscription(this)` directly, bypassing the mechanism in `NativeEventEmitter` that keeps track of the number of subscriptions. This meant that native event modules (subclasses of `RCTEventEmitter`) would keep sending events even after all the listeners had been removed. This wasn't a huge overhead, since these modules are singletons and only send one message over the bridge per event, regardless of the number of listeners, but it's still undesirable. This fixes the problem by routing the `EmitterSubscription.remove()` method through the `EventEmitter` so that `NativeEventEmitter` can apply the additional native calls. I've also improved the architecture so that each `NativeEventEmitter` uses its own `EventEmitter`, but they currently all still share the same `EventSubscriptionVendor` so that legacy code which registers events via `RCTDeviceEventEmitter` still works. Reviewed By: vjeux Differential Revision: D3292361 fbshipit-source-id: d60e881d50351523d2112473703bea826641cdef
166 lines
4.9 KiB
JavaScript
166 lines
4.9 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule EventEmitterWithHolding
|
|
* @flow
|
|
*/
|
|
'use strict';
|
|
|
|
import type EmitterSubscription from 'EmitterSubscription';
|
|
import type EventEmitter from 'EventEmitter';
|
|
import type EventHolder from 'EventHolder';
|
|
|
|
/**
|
|
* @class EventEmitterWithHolding
|
|
* @description
|
|
* An EventEmitterWithHolding decorates an event emitter and enables one to
|
|
* "hold" or cache events and then have a handler register later to actually
|
|
* handle them.
|
|
*
|
|
* This is separated into its own decorator so that only those who want to use
|
|
* the holding functionality have to and others can just use an emitter. Since
|
|
* it implements the emitter interface it can also be combined with anything
|
|
* that uses an emitter.
|
|
*/
|
|
class EventEmitterWithHolding {
|
|
|
|
_emitter: EventEmitter;
|
|
_eventHolder: EventHolder;
|
|
_currentEventToken: ?Object;
|
|
_emittingHeldEvents: boolean;
|
|
|
|
/**
|
|
* @constructor
|
|
* @param {object} emitter - The object responsible for emitting the actual
|
|
* events.
|
|
* @param {object} holder - The event holder that is responsible for holding
|
|
* and then emitting held events.
|
|
*/
|
|
constructor(emitter: EventEmitter, holder: EventHolder) {
|
|
this._emitter = emitter;
|
|
this._eventHolder = holder;
|
|
this._currentEventToken = null;
|
|
this._emittingHeldEvents = false;
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#addListener
|
|
*/
|
|
addListener(eventType: string, listener: Function, context: ?Object) {
|
|
return this._emitter.addListener(eventType, listener, context);
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#once
|
|
*/
|
|
once(eventType: string, listener: Function, context: ?Object) {
|
|
return this._emitter.once(eventType, listener, context);
|
|
}
|
|
|
|
/**
|
|
* Adds a listener to be invoked when events of the specified type are
|
|
* emitted. An optional calling context may be provided. The data arguments
|
|
* emitted will be passed to the listener function. In addition to subscribing
|
|
* to all subsequent events, this method will also handle any events that have
|
|
* already been emitted, held, and not released.
|
|
*
|
|
* @param {string} eventType - Name of the event to listen to
|
|
* @param {function} listener - Function to invoke when the specified event is
|
|
* emitted
|
|
* @param {*} context - Optional context object to use when invoking the
|
|
* listener
|
|
*
|
|
* @example
|
|
* emitter.emitAndHold('someEvent', 'abc');
|
|
*
|
|
* emitter.addRetroactiveListener('someEvent', function(message) {
|
|
* console.log(message);
|
|
* }); // logs 'abc'
|
|
*/
|
|
addRetroactiveListener(
|
|
eventType: string, listener: Function, context: ?Object): EmitterSubscription {
|
|
const subscription = this._emitter.addListener(eventType, listener, context);
|
|
|
|
this._emittingHeldEvents = true;
|
|
this._eventHolder.emitToListener(eventType, listener, context);
|
|
this._emittingHeldEvents = false;
|
|
|
|
return subscription;
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#removeAllListeners
|
|
*/
|
|
removeAllListeners(eventType: string) {
|
|
this._emitter.removeAllListeners(eventType);
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#removeCurrentListener
|
|
*/
|
|
removeCurrentListener() {
|
|
this._emitter.removeCurrentListener();
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#listeners
|
|
*/
|
|
listeners(eventType: string) /* TODO: Annotate return type here */ {
|
|
return this._emitter.listeners(eventType);
|
|
}
|
|
|
|
/**
|
|
* @see EventEmitter#emit
|
|
*/
|
|
emit(eventType: string, ...args: any) {
|
|
this._emitter.emit(eventType, ...args);
|
|
}
|
|
|
|
/**
|
|
* Emits an event of the given type with the given data, and holds that event
|
|
* in order to be able to dispatch it to a later subscriber when they say they
|
|
* want to handle held events.
|
|
*
|
|
* @param {string} eventType - Name of the event to emit
|
|
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
|
*
|
|
* @example
|
|
* emitter.emitAndHold('someEvent', 'abc');
|
|
*
|
|
* emitter.addRetroactiveListener('someEvent', function(message) {
|
|
* console.log(message);
|
|
* }); // logs 'abc'
|
|
*/
|
|
emitAndHold(eventType: string, ...args: any) {
|
|
this._currentEventToken = this._eventHolder.holdEvent(eventType, ...args);
|
|
this._emitter.emit(eventType, ...args);
|
|
this._currentEventToken = null;
|
|
}
|
|
|
|
/**
|
|
* @see EventHolder#releaseCurrentEvent
|
|
*/
|
|
releaseCurrentEvent() {
|
|
if (this._currentEventToken) {
|
|
this._eventHolder.releaseEvent(this._currentEventToken);
|
|
} else if (this._emittingHeldEvents) {
|
|
this._eventHolder.releaseCurrentEvent();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see EventHolder#releaseEventType
|
|
* @param {string} eventType
|
|
*/
|
|
releaseHeldEventType(eventType: string) {
|
|
this._eventHolder.releaseEventType(eventType);
|
|
}
|
|
}
|
|
|
|
module.exports = EventEmitterWithHolding;
|