Track currently replaying event (#22853)
* Track currently replaying event Co-authored-by: Dan Abramov <dan.abramov@me.com> * Add warnings Co-authored-by: Marco Salazar <salazarm@fb.com>
Dan Abramov committed
Dec 2, 2021 at 14:11 UTC
44f99d75fa4528b82810edd03eccf82f7ea21ea0
5 files changed
+55
-9
packages/react-dom/src/events/CurrentReplayingEvent.js
new
+42
@@ -0,0 +1,42 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its 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
+import type {AnyNativeEvent} from '../events/PluginModuleType';
10
+
11
+// This exists to avoid circular dependency between ReactDOMEventReplaying
12
+// and DOMPluginEventSystem.
13
+
14
+let currentReplayingEvent = null;
15
+
16
+export function setReplayingEvent(event: AnyNativeEvent): void {
17
+ if (__DEV__) {
18
+ if (currentReplayingEvent !== null) {
19
+ console.error(
20
+ 'Expected currently replaying event to be null. This error ' +
21
+ 'is likely caused by a bug in React. Please file an issue.',
22
+ );
23
+ }
24
+ }
25
+ currentReplayingEvent = event;
26
+}
27
+
28
+export function resetReplayingEvent(): void {
29
+ if (__DEV__) {
30
+ if (currentReplayingEvent === null) {
31
+ console.error(
32
+ 'Expected currently replaying event to not be null. This error ' +
33
+ 'is likely caused by a bug in React. Please file an issue.',
34
+ );
35
+ }
36
+ }
37
+ currentReplayingEvent = null;
38
+}
39
+
40
+export function isReplayingEvent(event: AnyNativeEvent): boolean {
41
+ return event === currentReplayingEvent;
42
+}
packages/react-dom/src/events/DOMPluginEventSystem.js
+3
-1
@@ -27,6 +27,7 @@ import {
27
IS_EVENT_HANDLE_NON_MANAGED_NODE,
28
IS_NON_DELEGATED,
29
} from './EventSystemFlags';
30
+import {isReplayingEvent} from './CurrentReplayingEvent';
31
32
import {
33
HostRoot,
@@ -557,7 +558,8 @@ export function dispatchEventForPluginEventSystem(
558
// for legacy FB support, where the expected behavior was to
559
// match React < 16 behavior of delegated clicks to the doc.
560
domEventName === 'click' &&
560
- (eventSystemFlags & SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE) === 0
561
+ (eventSystemFlags & SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE) === 0 &&
562
+ !isReplayingEvent(nativeEvent)
563
) {
564
deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer);
565
return;
packages/react-dom/src/events/EventSystemFlags.js
+2
-3
@@ -13,11 +13,10 @@ export const IS_EVENT_HANDLE_NON_MANAGED_NODE = 1;
13
export const IS_NON_DELEGATED = 1 << 1;
14
export const IS_CAPTURE_PHASE = 1 << 2;
15
export const IS_PASSIVE = 1 << 3;
16
-export const IS_REPLAYED = 1 << 4;
17
-export const IS_LEGACY_FB_SUPPORT_MODE = 1 << 5;
16
+export const IS_LEGACY_FB_SUPPORT_MODE = 1 << 4;
17
18
export const SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE =
20
- IS_LEGACY_FB_SUPPORT_MODE | IS_REPLAYED | IS_CAPTURE_PHASE;
19
+ IS_LEGACY_FB_SUPPORT_MODE | IS_CAPTURE_PHASE;
20
21
// We do not want to defer if the event system has already been
22
// set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when
packages/react-dom/src/events/ReactDOMEventReplaying.js
+6
-3
@@ -31,6 +31,7 @@ import {
31
findInstanceBlockingEvent,
32
return_targetInst,
33
} from './ReactDOMEventListener';
34
+import {setReplayingEvent, resetReplayingEvent} from './CurrentReplayingEvent';
35
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
36
import {
37
getInstanceFromNode,
@@ -91,8 +92,6 @@ type PointerEvent = Event & {
92
...
93
};
94
94
-import {IS_REPLAYED} from './EventSystemFlags';
95
-
95
type QueuedReplayableEvent = {|
96
blockedOn: null | Container | SuspenseInstance,
97
domEventName: DOMEventName,
@@ -180,7 +179,7 @@ function createQueuedReplayableEvent(
179
return {
180
blockedOn,
181
domEventName,
183
- eventSystemFlags: eventSystemFlags | IS_REPLAYED,
182
+ eventSystemFlags,
183
nativeEvent,
184
targetContainers: [targetContainer],
185
};
@@ -473,6 +472,7 @@ function attemptReplayContinuousQueuedEvent(
472
queuedEvent.nativeEvent,
473
);
474
if (nextBlockedOn === null) {
475
+ setReplayingEvent(queuedEvent.nativeEvent);
476
dispatchEventForPluginEventSystem(
477
queuedEvent.domEventName,
478
queuedEvent.eventSystemFlags,
@@ -480,6 +480,7 @@ function attemptReplayContinuousQueuedEvent(
480
return_targetInst,
481
targetContainer,
482
);
483
+ resetReplayingEvent();
484
} else {
485
// We're still blocked. Try again later.
486
const fiber = getInstanceFromNode(nextBlockedOn);
@@ -531,6 +532,7 @@ function replayUnblockedEvents() {
532
nextDiscreteEvent.nativeEvent,
533
);
534
if (nextBlockedOn === null) {
535
+ setReplayingEvent(nextDiscreteEvent.nativeEvent);
536
dispatchEventForPluginEventSystem(
537
nextDiscreteEvent.domEventName,
538
nextDiscreteEvent.eventSystemFlags,
@@ -538,6 +540,7 @@ function replayUnblockedEvents() {
540
return_targetInst,
541
targetContainer,
542
);
543
+ resetReplayingEvent();
544
} else {
545
// We're still blocked. Try again later.
546
nextDiscreteEvent.blockedOn = nextBlockedOn;
packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js
+2
-2
@@ -13,7 +13,7 @@ import type {DispatchQueue} from '../DOMPluginEventSystem';
13
import type {EventSystemFlags} from '../EventSystemFlags';
14
15
import {registerDirectEvent} from '../EventRegistry';
16
-import {IS_REPLAYED} from 'react-dom/src/events/EventSystemFlags';
16
+import {isReplayingEvent} from '../CurrentReplayingEvent';
17
import {SyntheticMouseEvent, SyntheticPointerEvent} from '../SyntheticEvent';
18
import {
19
getClosestInstanceFromNode,
@@ -54,7 +54,7 @@ function extractEvents(
54
const isOutEvent =
55
domEventName === 'mouseout' || domEventName === 'pointerout';
56
57
- if (isOverEvent && (eventSystemFlags & IS_REPLAYED) === 0) {
57
+ if (isOverEvent && !isReplayingEvent(nativeEvent)) {
58
// If this is an over event with a target, we might have already dispatched
59
// the event in the out event of the other target. If this is replayed,
60
// then it's because we couldn't dispatch against this target previously