main
js 153 lines 5.12 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {
11 AnyNativeEvent,
12 LegacyPluginModule,
13 } from './legacy-events/PluginModuleType';
14 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
15 import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType';
16 import type {
17 RNTopLevelEventType,
18 TopLevelType,
19 } from './legacy-events/TopLevelEventTypes';
20
21 import {
22 registrationNameModules,
23 plugins,
24 } from './legacy-events/EventPluginRegistry';
25 import {batchedUpdates} from './legacy-events/ReactGenericBatching';
26 import accumulateInto from './legacy-events/accumulateInto';
27
28 import getListener from './ReactNativeGetListener';
29 import {runEventsInBatch} from './legacy-events/EventBatching';
30
31 import {
32 RawEventEmitter,
33 dispatchNativeEvent,
34 } from 'react-native/react-private-interface';
35 import {getPublicInstance} from './ReactFiberConfigFabric';
36 import {enableNativeEventTargetEventDispatching} from './ReactNativeFeatureFlags';
37
38 export {getListener, registrationNameModules as registrationNames};
39
40 /**
41 * Allows registered plugins an opportunity to extract events from top-level
42 * native browser events.
43 *
44 * @return {*} An accumulation of synthetic events.
45 * @internal
46 */
47 function extractPluginEvents(
48 topLevelType: TopLevelType,
49 targetInst: null | Fiber,
50 nativeEvent: AnyNativeEvent,
51 nativeEventTarget: null | EventTarget,
52 ): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
53 let events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null = null;
54 const legacyPlugins = plugins as any as Array<
55 LegacyPluginModule<AnyNativeEvent>,
56 >;
57 for (let i = 0; i < legacyPlugins.length; i++) {
58 // Not every plugin in the ordering may be loaded at runtime.
59 const possiblePlugin = legacyPlugins[i];
60 if (possiblePlugin) {
61 const extractedEvents = possiblePlugin.extractEvents(
62 topLevelType,
63 targetInst,
64 nativeEvent,
65 nativeEventTarget,
66 );
67 if (extractedEvents) {
68 events = accumulateInto(events, extractedEvents);
69 }
70 }
71 }
72 return events;
73 }
74
75 function runExtractedPluginEventsInBatch(
76 topLevelType: TopLevelType,
77 targetInst: null | Fiber,
78 nativeEvent: AnyNativeEvent,
79 nativeEventTarget: null | EventTarget,
80 ) {
81 const events = extractPluginEvents(
82 topLevelType,
83 targetInst,
84 nativeEvent,
85 nativeEventTarget,
86 );
87 runEventsInBatch(events);
88 }
89
90 export function dispatchEvent(
91 target: null | Object,
92 topLevelType: RNTopLevelEventType,
93 nativeEventParam: mixed,
94 ) {
95 const nativeEvent: AnyNativeEvent =
96 nativeEventParam != null && typeof nativeEventParam === 'object'
97 ? (nativeEventParam as any)
98 : {};
99 const targetFiber = target as null | Fiber;
100
101 let eventTarget = null;
102 if (targetFiber != null) {
103 const stateNode = targetFiber.stateNode;
104 // Guard against Fiber being unmounted
105 if (stateNode != null) {
106 // $FlowExpectedError[incompatible-type] public instances in Fabric do not implement `EventTarget` yet.
107 eventTarget = getPublicInstance(stateNode) as EventTarget;
108 }
109 }
110
111 batchedUpdates(function () {
112 // Emit event to the RawEventEmitter. This is an unused-by-default EventEmitter
113 // that can be used to instrument event performance monitoring (primarily - could be useful
114 // for other things too).
115 //
116 // NOTE: this merely emits events into the EventEmitter below.
117 // If *you* do not add listeners to the `RawEventEmitter`,
118 // then all of these emitted events will just blackhole and are no-ops.
119 // It is available (although not officially supported... yet) if you want to collect
120 // perf data on event latency in your application, and could also be useful for debugging
121 // low-level events issues.
122 //
123 // If you do not have any event perf monitoring and are extremely concerned about event perf,
124 // it is safe to disable these "emit" statements; it will prevent checking the size of
125 // an empty array twice and prevent two no-ops. Practically the overhead is so low that
126 // we don't think it's worth thinking about in prod; your perf issues probably lie elsewhere.
127 //
128 // We emit two events here: one for listeners to this specific event,
129 // and one for the catchall listener '*', for any listeners that want
130 // to be notified for all events.
131 // Note that extracted events are *not* emitted,
132 // only events that have a 1:1 mapping with a native event, at least for now.
133 const event = {eventName: topLevelType, nativeEvent};
134 RawEventEmitter.emit(topLevelType, event);
135 RawEventEmitter.emit('*', event);
136
137 if (enableNativeEventTargetEventDispatching()) {
138 if (eventTarget != null) {
139 dispatchNativeEvent(eventTarget, topLevelType, nativeEvent);
140 }
141 } else {
142 // Heritage plugin event system
143 runExtractedPluginEventsInBatch(
144 topLevelType,
145 targetFiber,
146 nativeEvent,
147 eventTarget,
148 );
149 }
150 });
151 // React Native doesn't use ReactControlledComponent but if it did, here's
152 // where it would do it.
153 }