main
js 210 lines 6.01 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
8 import isArray from 'shared/isArray';
9
10 import {runWithFiberInDEV} from 'react-reconciler/src/ReactCurrentFiber';
11
12 let hasError = false;
13 let caughtError = null;
14
15 export let getFiberCurrentPropsFromNode = null;
16 export let getInstanceFromNode = null;
17 export let getNodeFromInstance = null;
18
19 export function setComponentTree(
20 getFiberCurrentPropsFromNodeImpl,
21 getInstanceFromNodeImpl,
22 getNodeFromInstanceImpl,
23 ) {
24 getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
25 getInstanceFromNode = getInstanceFromNodeImpl;
26 getNodeFromInstance = getNodeFromInstanceImpl;
27 if (__DEV__) {
28 if (!getNodeFromInstance || !getInstanceFromNode) {
29 console.error(
30 'Injected ' +
31 'module is missing getNodeFromInstance or getInstanceFromNode.',
32 );
33 }
34 }
35 }
36
37 function validateEventDispatches(event) {
38 if (__DEV__) {
39 const dispatchListeners = event._dispatchListeners;
40 const dispatchInstances = event._dispatchInstances;
41
42 const listenersIsArr = isArray(dispatchListeners);
43 const listenersLen = listenersIsArr
44 ? dispatchListeners.length
45 : dispatchListeners
46 ? 1
47 : 0;
48
49 const instancesIsArr = isArray(dispatchInstances);
50 const instancesLen = instancesIsArr
51 ? dispatchInstances.length
52 : dispatchInstances
53 ? 1
54 : 0;
55
56 if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
57 console.error('EventPluginUtils: Invalid `event`.');
58 }
59 }
60 }
61
62 /**
63 * Dispatch the event to the listener.
64 * @param {SyntheticEvent} event SyntheticEvent to handle
65 * @param {function} listener Application-level callback
66 * @param {*} inst Internal component instance
67 */
68 export function executeDispatch(event, listener, inst) {
69 event.currentTarget = getNodeFromInstance(inst);
70 const currentEvent = global.event;
71 global.event = event;
72
73 try {
74 listener(event);
75 } catch (error) {
76 if (!hasError) {
77 hasError = true;
78 caughtError = error;
79 } else {
80 // TODO: Make sure this error gets logged somehow.
81 }
82 }
83
84 global.event = currentEvent;
85 event.currentTarget = null;
86 }
87
88 /**
89 * Standard/simple iteration through an event's collected dispatches.
90 */
91 export function executeDispatchesInOrder(event) {
92 const dispatchListeners = event._dispatchListeners;
93 const dispatchInstances = event._dispatchInstances;
94 if (__DEV__) {
95 validateEventDispatches(event);
96 }
97 if (isArray(dispatchListeners)) {
98 for (let i = 0; i < dispatchListeners.length; i++) {
99 if (event.isPropagationStopped()) {
100 break;
101 }
102 // Listeners and Instances are two parallel arrays that are always in sync.
103 const listener = dispatchListeners[i];
104 const instance = dispatchInstances[i];
105 if (__DEV__ && instance !== null) {
106 runWithFiberInDEV(instance, executeDispatch, event, listener, instance);
107 } else {
108 executeDispatch(event, listener, instance);
109 }
110 }
111 } else if (dispatchListeners) {
112 const listener = dispatchListeners;
113 const instance = dispatchInstances;
114 if (__DEV__ && instance !== null) {
115 runWithFiberInDEV(instance, executeDispatch, event, listener, instance);
116 } else {
117 executeDispatch(event, listener, instance);
118 }
119 }
120 event._dispatchListeners = null;
121 event._dispatchInstances = null;
122 }
123
124 /**
125 * Standard/simple iteration through an event's collected dispatches, but stops
126 * at the first dispatch execution returning true, and returns that id.
127 *
128 * @return {?string} id of the first dispatch execution who's listener returns
129 * true, or null if no listener returned true.
130 */
131 function executeDispatchesInOrderStopAtTrueImpl(event) {
132 const dispatchListeners = event._dispatchListeners;
133 const dispatchInstances = event._dispatchInstances;
134 if (__DEV__) {
135 validateEventDispatches(event);
136 }
137 if (isArray(dispatchListeners)) {
138 for (let i = 0; i < dispatchListeners.length; i++) {
139 if (event.isPropagationStopped()) {
140 break;
141 }
142 // Listeners and Instances are two parallel arrays that are always in sync.
143 if (dispatchListeners[i](event, dispatchInstances[i])) {
144 return dispatchInstances[i];
145 }
146 }
147 } else if (dispatchListeners) {
148 if (dispatchListeners(event, dispatchInstances)) {
149 return dispatchInstances;
150 }
151 }
152 return null;
153 }
154
155 /**
156 * @see executeDispatchesInOrderStopAtTrueImpl
157 */
158 export function executeDispatchesInOrderStopAtTrue(event) {
159 const ret = executeDispatchesInOrderStopAtTrueImpl(event);
160 event._dispatchInstances = null;
161 event._dispatchListeners = null;
162 return ret;
163 }
164
165 /**
166 * Execution of a "direct" dispatch - there must be at most one dispatch
167 * accumulated on the event or it is considered an error. It doesn't really make
168 * sense for an event with multiple dispatches (bubbled) to keep track of the
169 * return values at each dispatch execution, but it does tend to make sense when
170 * dealing with "direct" dispatches.
171 *
172 * @return {*} The return value of executing the single dispatch.
173 */
174 export function executeDirectDispatch(event) {
175 if (__DEV__) {
176 validateEventDispatches(event);
177 }
178 const dispatchListener = event._dispatchListeners;
179 const dispatchInstance = event._dispatchInstances;
180
181 if (isArray(dispatchListener)) {
182 throw new Error('Invalid `event`.');
183 }
184
185 event.currentTarget = dispatchListener
186 ? getNodeFromInstance(dispatchInstance)
187 : null;
188 const res = dispatchListener ? dispatchListener(event) : null;
189 event.currentTarget = null;
190 event._dispatchListeners = null;
191 event._dispatchInstances = null;
192 return res;
193 }
194
195 /**
196 * @param {SyntheticEvent} event
197 * @return {boolean} True iff number of dispatches accumulated is greater than 0.
198 */
199 export function hasDispatches(event) {
200 return !!event._dispatchListeners;
201 }
202
203 export function rethrowCaughtError() {
204 if (hasError) {
205 const error = caughtError;
206 hasError = false;
207 caughtError = null;
208 throw error;
209 }
210 }