Skip to content

Commit

Permalink
React Native raw event EventEmitter - intended for app-specific perf …
Browse files Browse the repository at this point in the history
…listeners and debugging (facebook#23232)

* RawEventEmitter: new event perf profiling mechanism outside of Pressability to capture all touch events, and other event types

* sync

* concise notation

* Move event telemetry event emitter call from Plugin to ReactFabricEventEmitter, to reduce reliance on the plugin system and move the emit call further into the core

* Backout changes to ReactNativeEventPluginOrder

* Properly flow typing event emitter, and emit event to two channels: named and catchall

* fix typing for event name string

* fix typing for event name string

* fix flow

* Add more comments about how the event telemetry system works

* Add more comments about how the event telemetry system works

* rename to RawEventTelemetryEventEmitterOffByDefault

* yarn prettier-all

* rename event

* comments

* improve flow types

* renamed file
  • Loading branch information
JoshuaGross authored and nevilm-lt committed Apr 22, 2022
1 parent 38d33f5 commit 6af8570
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
29 changes: 28 additions & 1 deletion packages/react-native-renderer/src/ReactFabricEventEmitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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};

/**
Expand Down Expand Up @@ -73,7 +75,7 @@ function runExtractedPluginEventsInBatch(

export function dispatchEvent(
target: null | Object,
topLevelType: TopLevelType,
topLevelType: RNTopLevelEventType,
nativeEvent: AnyNativeEvent,
) {
const targetFiber = (target: null | Fiber);
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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};
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ module.exports = {
get legacySendAccessibilityEvent() {
return require('./legacySendAccessibilityEvent');
},
get RawEventEmitter() {
return require('./RawEventEmitter').default;
},
};
11 changes: 11 additions & 0 deletions scripts/flow/react-native-host-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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' {
Expand Down

0 comments on commit 6af8570

Please sign in to comment.