diff --git a/packages/react-native-renderer/src/ReactFabricEventEmitter.js b/packages/react-native-renderer/src/ReactFabricEventEmitter.js index 15798d8f777e..d8492156fdc7 100644 --- a/packages/react-native-renderer/src/ReactFabricEventEmitter.js +++ b/packages/react-native-renderer/src/ReactFabricEventEmitter.js @@ -21,6 +21,8 @@ import {plugins} from './legacy-events/EventPluginRegistry'; import getListener from './ReactNativeGetListener'; import {runEventsInBatch} from './legacy-events/EventBatching'; +import {RawEventEmitter} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; + export {getListener, registrationNameModules as registrationNames}; /** @@ -73,7 +75,7 @@ function runExtractedPluginEventsInBatch( export function dispatchEvent( target: null | Object, - topLevelType: TopLevelType, + topLevelType: RNTopLevelEventType, nativeEvent: AnyNativeEvent, ) { const targetFiber = (target: null | Fiber); @@ -88,6 +90,31 @@ export function dispatchEvent( } batchedUpdates(function() { + // Emit event to the RawEventEmitter. This is an unused-by-default EventEmitter + // that can be used to instrument event performance monitoring (primarily - could be useful + // for other things too). + // + // NOTE: this merely emits events into the EventEmitter below. + // If *you* do not add listeners to the `RawEventEmitter`, + // then all of these emitted events will just blackhole and are no-ops. + // It is available (although not officially supported... yet) if you want to collect + // perf data on event latency in your application, and could also be useful for debugging + // low-level events issues. + // + // If you do not have any event perf monitoring and are extremely concerned about event perf, + // it is safe to disable these "emit" statements; it will prevent checking the size of + // an empty array twice and prevent two no-ops. Practically the overhead is so low that + // we don't think it's worth thinking about in prod; your perf issues probably lie elsewhere. + // + // We emit two events here: one for listeners to this specific event, + // and one for the catchall listener '*', for any listeners that want + // to be notified for all events. + // Note that extracted events are *not* emitted, + // only events that have a 1:1 mapping with a native event, at least for now. + const event = {eventName: topLevelType, nativeEvent}; + RawEventEmitter.emit(topLevelType, event); + RawEventEmitter.emit('*', event); + // Heritage plugin event system runExtractedPluginEventsInBatch( topLevelType, diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RawEventEmitter.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RawEventEmitter.js new file mode 100644 index 000000000000..abb419842bee --- /dev/null +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RawEventEmitter.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +// See the react-native repository for a full implementation. +// However, this is just an EventEmitter. +const RawEventEmitter = { + emit: jest.fn(), +}; + +module.exports = {default: RawEventEmitter}; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js index 3def12a904f6..3eaf3a5a3805 100644 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js @@ -41,4 +41,7 @@ module.exports = { get legacySendAccessibilityEvent() { return require('./legacySendAccessibilityEvent'); }, + get RawEventEmitter() { + return require('./RawEventEmitter').default; + }, }; diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index 770b77a84082..c7a2fc8dc321 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -21,6 +21,13 @@ import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; type DeepDifferOptions = {|+unsafelyIgnoreFunctions?: boolean|}; +type RawEventEmitterEvent = $ReadOnly<{| + eventName: string, + // We expect, but do not/cannot require, that nativeEvent is an object + // with the properties: key, elementType (string), type (string), tag (numeric), + // and a stateNode of the native element/Fiber the event was emitted to. + nativeEvent: {[string]: mixed}, +|}>; declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' { declare export function deepDiffer( @@ -127,6 +134,10 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' get: (name: string) => ReactNativeBaseComponentViewConfig, ... }; + declare export var RawEventEmitter: { + emit: (channel: string, event: RawEventEmitterEvent) => string, + ... + }; } declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore' {