Skip to content

Commit

Permalink
fix(database): react-native 0.73 compatibility
Browse files Browse the repository at this point in the history
upstream EventEmitter made registrations fully private, and we
were relying on them to implement specific-listener-removal to
meet a firebase-js-sdk API contract

implemented listener registration accounting in situ in place of
previous style relying on upstream EventEmitter implementation
  • Loading branch information
mikehardy committed Dec 8, 2023
1 parent a899390 commit 788cc22
Showing 1 changed file with 43 additions and 10 deletions.
53 changes: 43 additions & 10 deletions packages/database/lib/DatabaseSyncTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,32 @@ class DatabaseSyncTree {
this._tree = {};
this._reverseLookup = {};

// we need to be able to register multiple listeners for a single event,
// *then delete a specific single listener for that event*, so we have to
// be able to identify specific listeners for removal which means we need
// to mirror the private registration accounting from upstream EventEmitter
// so we have access here and can do our single emitter removal
// This is a map of emitter-key <-> set of listener registrations
// The listener registrations have { context, listener, remove() }
this._registry = {};

SharedEventEmitter.addListener('database_sync_event', this._handleSyncEvent.bind(this));
}

get native() {
return NativeModules.RNFBDatabaseQueryModule;
}

// from upstream EventEmitter: initialize registrations for an emitter key
_allocate(registry, eventType) {
let registrations = registry[eventType];
if (registrations == null) {
registrations = new Set();
registry[eventType] = registrations;
}
return registrations;
}

/**
* Handles an incoming event from native
* @param event
Expand Down Expand Up @@ -132,6 +151,13 @@ class DatabaseSyncTree {
if (isString(registrations)) {
this.removeRegistration(registrations);
SharedEventEmitter.removeAllListeners(registrations);

// mirror upstream accounting - clear out all registrations for this key
if (registrations == null) {
this._registry = {};
} else {
delete this._registry[registrations];
}
return 1;
}

Expand All @@ -141,6 +167,12 @@ class DatabaseSyncTree {
for (let i = 0, len = registrations.length; i < len; i++) {
this.removeRegistration(registrations[i]);
SharedEventEmitter.removeAllListeners(registrations[i]);
// mirror upstream accounting - clear out all registrations for this key
if (registrations[i] == null) {
this._registry = {};
} else {
delete this._registry[registrations[i]];
}
}

return registrations.length;
Expand All @@ -163,15 +195,10 @@ class DatabaseSyncTree {
const registration = registrations[i];
let subscriptions;

// EventEmitter in react-native < 0.70 had a `_subscriber` property with a method for subscriptions by type...
if (SharedEventEmitter._subscriber) {
subscriptions = SharedEventEmitter._subscriber.getSubscriptionsForType(registration);
} else {
// ...react-native 0.70 now stores subscribers as a map of Sets by type in `_registry`
const registrySubscriptionsSet = SharedEventEmitter._registry[registration];
if (registrySubscriptionsSet) {
subscriptions = Array.from(registrySubscriptionsSet);
}
// get all registrations for this key so we can find our specific listener
const registrySubscriptionsSet = this._registry[registration];
if (registrySubscriptionsSet) {
subscriptions = Array.from(registrySubscriptionsSet);
}

if (subscriptions) {
Expand All @@ -180,6 +207,7 @@ class DatabaseSyncTree {
// The subscription may have been removed during this event loop.
// its listener matches the listener in method parameters
if (subscription && subscription.listener === listener) {
this._registry[registration].delete(subscription);
subscription.remove();
removed.push(registration);
this.removeRegistration(registration);
Expand Down Expand Up @@ -278,7 +306,12 @@ class DatabaseSyncTree {
subscription.remove();
});
} else {
SharedEventEmitter.addListener(eventRegistrationKey, listener);
const registration = SharedEventEmitter.addListener(eventRegistrationKey, listener);

// add this listener registration info to our emitter-key map
// in case we need to identify and remove a specific listener later
const registrations = this._allocate(this._registry, eventRegistrationKey);
registrations.add(registration);
}

return eventRegistrationKey;
Expand Down

0 comments on commit 788cc22

Please sign in to comment.