react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
87 lines (83 loc) • 2.97 kB
JavaScript
;
/**
* Routes CSS events emitted by the native side to the objects interested in a
* given view. Nested animated components can share a view tag, so every tag
* keeps a set of subscribers rather than a single one.
*
* A view that unmounts is retired instead of unregistered: it stops being a
* subscriber, but still hears the batch after, because the engine emits its
* cancel as it tears down and that batch arrives once the view is gone.
*/
class CSSCallbacksRegistry {
subscribersByTag = new Map();
retiringByTag = new Map();
register(viewTag, subscriber) {
this.retiringByTag.get(viewTag)?.delete(subscriber);
addSubscriber(this.subscribersByTag, viewTag, subscriber);
}
unregister(viewTag, subscriber) {
const subscribers = this.subscribersByTag.get(viewTag);
if (!subscribers) {
return;
}
subscribers.delete(subscriber);
if (subscribers.size === 0) {
this.subscribersByTag.delete(viewTag);
}
}
/**
* Unregistering now, rather than after the batch it still hears, is what lets
* a view that mounts again stay subscribed by simply registering.
*/
retire(viewTag, subscriber) {
this.unregister(viewTag, subscriber);
addSubscriber(this.retiringByTag, viewTag, subscriber);
}
dispatch(events) {
// Taken up front, so a view retired by a callback in this batch hears the
// next one rather than this one.
const retiring = this.retiringByTag;
this.retiringByTag = new Map();
for (const event of events) {
const subscribers = subscribersFor(event.tag, this.subscribersByTag, retiring);
// Absent once the event outlives the view it was emitted for.
if (!subscribers) {
continue;
}
for (const subscriber of subscribers) {
try {
subscriber.handleCSSEvent(event);
} catch (error) {
// Reported the way an uncaught error would be anyway, so one broken
// callback stays fatal without starving the views queued behind it.
// @ts-expect-error React Native's `ErrorUtils` are hidden from the global scope.
globalThis.ErrorUtils.reportFatalError(error);
}
}
}
}
clear() {
this.subscribersByTag.clear();
this.retiringByTag.clear();
}
}
function addSubscriber(byTag, viewTag, subscriber) {
const subscribers = byTag.get(viewTag);
if (subscribers) {
subscribers.add(subscriber);
} else {
byTag.set(viewTag, new Set([subscriber]));
}
}
/** One set, because a view that registers again while retiring is in both. */
function subscribersFor(viewTag, subscribed, retiring) {
const current = subscribed.get(viewTag);
const retired = retiring.get(viewTag);
if (!retired) {
return current;
}
return current ? new Set([...current, ...retired]) : retired;
}
const cssCallbacksRegistry = new CSSCallbacksRegistry();
export default cssCallbacksRegistry;
//# sourceMappingURL=CSSCallbacksRegistry.js.map