[Flare] remove stopLocalPropagation option + modify responder ownership (#15889)
Dominic Gannaway committed
Jun 14, 2019 at 23:11 UTC
353e0ee47492c42160aab81e60a59b08e40bb0b3
11 files changed
+46
-106
packages/react-dom/src/events/DOMEventResponderSystem.js
+35
-49
@@ -107,11 +107,8 @@ const eventListeners:
107
($Shape<PartialEventObject>) => void,
108
> = new PossiblyWeakMap();
109
110
-const responderOwners: Map<
111
- ReactEventResponder,
112
- ReactEventComponentInstance,
113
-> = new Map();
110
let globalOwner = null;
111
+let continueLocalPropagation = false;
112
113
let currentTimeStamp = 0;
114
let currentTimers = new Map();
@@ -308,12 +305,7 @@ const eventResponderContext: ReactResponderContext = {
305
},
306
hasOwnership(): boolean {
307
validateResponderContext();
311
- const responder = ((currentInstance: any): ReactEventComponentInstance)
312
- .responder;
313
- return (
314
- globalOwner === currentInstance ||
315
- responderOwners.get(responder) === currentInstance
316
- );
308
+ return globalOwner === currentInstance;
309
},
310
requestGlobalOwnership(): boolean {
311
validateResponderContext();
@@ -321,18 +313,7 @@ const eventResponderContext: ReactResponderContext = {
313
return false;
314
}
315
globalOwner = currentInstance;
324
- triggerOwnershipListeners(null);
325
- return true;
326
- },
327
- requestResponderOwnership(): boolean {
328
- validateResponderContext();
329
- const eventComponentInstance = ((currentInstance: any): ReactEventComponentInstance);
330
- const responder = eventComponentInstance.responder;
331
- if (responderOwners.has(responder)) {
332
- return false;
333
- }
334
- responderOwners.set(responder, eventComponentInstance);
335
- triggerOwnershipListeners(responder);
316
+ triggerOwnershipListeners();
317
return true;
318
},
319
releaseOwnership(): boolean {
@@ -433,6 +414,10 @@ const eventResponderContext: ReactResponderContext = {
414
}
415
return false;
416
},
417
+ continueLocalPropagation() {
418
+ validateResponderContext();
419
+ continueLocalPropagation = true;
420
+ },
421
};
422
423
function collectFocusableElements(
@@ -485,22 +470,12 @@ function getActiveDocument(): Document {
470
function releaseOwnershipForEventComponentInstance(
471
eventComponentInstance: ReactEventComponentInstance,
472
): boolean {
488
- const responder = eventComponentInstance.responder;
489
- let triggerOwnershipListenersWith;
490
- if (responderOwners.get(responder) === eventComponentInstance) {
491
- responderOwners.delete(responder);
492
- triggerOwnershipListenersWith = responder;
493
- }
473
if (globalOwner === eventComponentInstance) {
474
globalOwner = null;
496
- triggerOwnershipListenersWith = null;
497
- }
498
- if (triggerOwnershipListenersWith !== undefined) {
499
- triggerOwnershipListeners(triggerOwnershipListenersWith);
475
+ triggerOwnershipListeners();
476
return true;
501
- } else {
502
- return false;
477
}
478
+ return false;
479
}
480
481
function isFiberHostComponentFocusable(fiber: Fiber): boolean {
@@ -725,10 +700,10 @@ function getRootEventResponderInstances(
700
701
function shouldSkipEventComponent(
702
eventResponderInstance: ReactEventComponentInstance,
703
+ responder: ReactEventResponder,
704
propagatedEventResponders: null | Set<ReactEventResponder>,
705
): boolean {
730
- const responder = eventResponderInstance.responder;
731
- if (propagatedEventResponders !== null && responder.stopLocalPropagation) {
706
+ if (propagatedEventResponders !== null) {
707
if (propagatedEventResponders.has(responder)) {
708
return true;
709
}
@@ -737,15 +712,19 @@ function shouldSkipEventComponent(
712
if (globalOwner && globalOwner !== eventResponderInstance) {
713
return true;
714
}
740
- if (
741
- responderOwners.has(responder) &&
742
- responderOwners.get(responder) !== eventResponderInstance
743
- ) {
744
- return true;
745
- }
715
return false;
716
}
717
718
+function checkForLocalPropagationContinuation(
719
+ responder: ReactEventResponder,
720
+ propagatedEventResponders: Set<ReactEventResponder>,
721
+) {
722
+ if (continueLocalPropagation === true) {
723
+ propagatedEventResponders.delete(responder);
724
+ continueLocalPropagation = false;
725
+ }
726
+}
727
+
728
function traverseAndHandleEventResponderInstances(
729
topLevelType: DOMTopLevelEventType,
730
targetFiber: null | Fiber,
@@ -795,6 +774,7 @@ function traverseAndHandleEventResponderInstances(
774
if (
775
shouldSkipEventComponent(
776
targetEventResponderInstance,
777
+ responder,
778
propagatedEventResponders,
779
)
780
) {
@@ -802,6 +782,10 @@ function traverseAndHandleEventResponderInstances(
782
}
783
currentInstance = targetEventResponderInstance;
784
eventListener(responderEvent, eventResponderContext, props, state);
785
+ checkForLocalPropagationContinuation(
786
+ responder,
787
+ propagatedEventResponders,
788
+ );
789
}
790
}
791
// We clean propagated event responders between phases.
@@ -815,6 +799,7 @@ function traverseAndHandleEventResponderInstances(
799
if (
800
shouldSkipEventComponent(
801
targetEventResponderInstance,
802
+ responder,
803
propagatedEventResponders,
804
)
805
) {
@@ -822,6 +807,10 @@ function traverseAndHandleEventResponderInstances(
807
}
808
currentInstance = targetEventResponderInstance;
809
eventListener(responderEvent, eventResponderContext, props, state);
810
+ checkForLocalPropagationContinuation(
811
+ responder,
812
+ propagatedEventResponders,
813
+ );
814
}
815
}
816
}
@@ -836,7 +825,9 @@ function traverseAndHandleEventResponderInstances(
825
const {responder, props, state} = rootEventResponderInstance;
826
const eventListener = responder.onRootEvent;
827
if (eventListener !== undefined) {
839
- if (shouldSkipEventComponent(rootEventResponderInstance, null)) {
828
+ if (
829
+ shouldSkipEventComponent(rootEventResponderInstance, responder, null)
830
+ ) {
831
continue;
832
}
833
currentInstance = rootEventResponderInstance;
@@ -846,18 +837,13 @@ function traverseAndHandleEventResponderInstances(
837
}
838
}
839
849
-function triggerOwnershipListeners(
850
- limitByResponder: null | ReactEventResponder,
851
-): void {
840
+function triggerOwnershipListeners(): void {
841
const listeningInstances = Array.from(ownershipChangeListeners);
842
const previousInstance = currentInstance;
843
try {
844
for (let i = 0; i < listeningInstances.length; i++) {
845
const instance = listeningInstances[i];
846
const {props, responder, state} = instance;
858
- if (limitByResponder !== null && limitByResponder !== responder) {
859
- continue;
860
- }
847
currentInstance = instance;
848
const onOwnershipChange = responder.onOwnershipChange;
849
if (onOwnershipChange !== undefined) {
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+7
-6
@@ -27,7 +27,6 @@ function createReactEventComponent({
27
onMount,
28
onUnmount,
29
onOwnershipChange,
30
- stopLocalPropagation,
30
allowMultipleHostChildren,
31
}) {
32
const testEventResponder = {
@@ -40,7 +39,6 @@ function createReactEventComponent({
39
onMount,
40
onUnmount,
41
onOwnershipChange,
43
- stopLocalPropagation: stopLocalPropagation || false,
42
allowMultipleHostChildren: allowMultipleHostChildren || false,
43
};
44
@@ -215,6 +213,7 @@ describe('DOMEventResponderSystem', () => {
213
const ClickEventComponent = createReactEventComponent({
214
targetEventTypes: ['click'],
215
onEvent: (event, context, props) => {
216
+ context.continueLocalPropagation();
217
eventResponderFiredCount++;
218
eventLog.push({
219
name: event.type,
@@ -224,6 +223,7 @@ describe('DOMEventResponderSystem', () => {
223
});
224
},
225
onEventCapture: (event, context, props) => {
226
+ context.continueLocalPropagation();
227
eventResponderFiredCount++;
228
eventLog.push({
229
name: event.type,
@@ -324,19 +324,20 @@ describe('DOMEventResponderSystem', () => {
324
]);
325
});
326
327
- it('nested event responders should fire in the correct order without stopLocalPropagation', () => {
327
+ it('nested event responders should fire in the correct order with continueLocalPropagation', () => {
328
let eventLog = [];
329
const buttonRef = React.createRef();
330
331
const ClickEventComponent = createReactEventComponent({
332
targetEventTypes: ['click'],
333
onEvent: (event, context, props) => {
334
+ context.continueLocalPropagation();
335
eventLog.push(`${props.name} [bubble]`);
336
},
337
onEventCapture: (event, context, props) => {
338
+ context.continueLocalPropagation();
339
eventLog.push(`${props.name} [capture]`);
340
},
339
- stopLocalPropagation: false,
341
});
342
343
const Test = () => (
@@ -361,7 +362,7 @@ describe('DOMEventResponderSystem', () => {
362
]);
363
});
364
364
- it('nested event responders should fire in the correct order with stopLocalPropagation', () => {
365
+ it('nested event responders should fire in the correct order', () => {
366
let eventLog = [];
367
const buttonRef = React.createRef();
368
@@ -373,7 +374,6 @@ describe('DOMEventResponderSystem', () => {
374
onEventCapture: (event, context, props) => {
375
eventLog.push(`${props.name} [capture]`);
376
},
376
- stopLocalPropagation: true,
377
});
378
379
const Test = () => (
@@ -686,6 +686,7 @@ describe('DOMEventResponderSystem', () => {
686
const EventComponent = createReactEventComponent({
687
targetEventTypes: ['pointerout'],
688
onEvent: (event, context) => {
689
+ context.continueLocalPropagation();
690
const isWithin = context.isTargetWithinEventResponderScope(
691
event.nativeEvent.relatedTarget,
692
);
packages/react-events/README.md
-14
@@ -82,12 +82,6 @@ Called before an Event Component in unmounted.
82
83
Defines the DOM events to listen to on the root of the app.
84
85
-### stopLocalPropagation: boolean
86
-
87
-Defines whether or not synthetic events propagate to other Event Components *of
88
-the same type*. This has no effect on propagation of the source DOM events or
89
-the synthetic events dispatched to Event Components of different types.
90
-
85
### targetEventTypes?: Array<ResponderEventType>
86
87
Defines the DOM events to listen to within the Event Component subtree.
@@ -159,14 +153,6 @@ has global ownership, only that instance and its responder are active. To releas
153
either `releaseOwnership()` must be called or the Event Component instance that had global ownership must be
154
unmounted. Calling `requestGlobalOwnership` also returns `true`/`false` if the request was successful.
155
162
-### requestResponderOwnership(): boolean
163
-
164
-The current Event Component instance can request responder ownership within the event system. When an Event Component
165
-instance has responder ownership, all other Event Component instances that have the same responder as the Event Component
166
-instance will no longer be active. To release ownership to other event responders, either `releaseOwnership()` must be
167
-called or the Event Component instance that had global ownership must be unmounted. Calling `requestResponderOwnership`
168
-also returns `true`/`false` if the request was successful.
169
-
156
### setTimeout(func: () => void, delay: number): Symbol
157
158
This can be used to dispatch async events, e.g., those that fire after a delay.
packages/react-events/src/Drag.js
-1
@@ -98,7 +98,6 @@ const DragResponder = {
98
};
99
},
100
allowMultipleHostChildren: false,
101
- stopLocalPropagation: true,
101
onEvent(
102
event: ReactResponderEvent,
103
context: ReactResponderContext,
packages/react-events/src/Focus.js
-1
@@ -228,7 +228,6 @@ const FocusResponder = {
228
};
229
},
230
allowMultipleHostChildren: false,
231
- stopLocalPropagation: true,
231
onEvent(
232
event: ReactResponderEvent,
233
context: ReactResponderContext,
packages/react-events/src/FocusScope.js
+3
-29
@@ -6,12 +6,10 @@
6
*
7
* @flow
8
*/
9
-
9
import type {
10
ReactResponderEvent,
11
ReactResponderContext,
12
} from 'shared/ReactTypes';
14
-
13
import React from 'react';
14
15
type FocusScopeProps = {
@@ -56,7 +54,6 @@ const FocusScopeResponder = {
54
};
55
},
56
allowMultipleHostChildren: true,
59
- stopLocalPropagation: false,
57
onEvent(
58
event: ReactResponderEvent,
59
context: ReactResponderContext,
@@ -64,12 +61,7 @@ const FocusScopeResponder = {
61
state: FocusScopeState,
62
) {
63
const {type, nativeEvent} = event;
67
- const hasOwnership =
68
- context.hasOwnership() || context.requestResponderOwnership();
64
70
- if (!hasOwnership) {
71
- return;
72
- }
65
if (type === 'keydown' && nativeEvent.key === 'Tab') {
66
const focusedElement = context.getActiveDocument().activeElement;
67
if (
@@ -92,7 +84,7 @@ const FocusScopeResponder = {
84
nextElement = elements[lastPosition];
85
} else {
86
// Out of bounds
95
- context.releaseOwnership();
87
+ context.continueLocalPropagation();
88
return;
89
}
90
} else {
@@ -104,19 +96,14 @@ const FocusScopeResponder = {
96
nextElement = elements[0];
97
} else {
98
// Out of bounds
107
- context.releaseOwnership();
99
+ context.continueLocalPropagation();
100
return;
101
}
102
} else {
103
nextElement = elements[position + 1];
104
}
105
}
114
- // If this element is possibly inside the scope of another
115
- // FocusScope responder or is out of bounds, then we release ownership.
106
if (nextElement !== null) {
117
- if (!context.isTargetWithinEventResponderScope(nextElement)) {
118
- context.releaseOwnership();
119
- }
107
focusElement(nextElement);
108
state.currentFocusedNode = nextElement;
109
((nativeEvent: any): KeyboardEvent).preventDefault();
@@ -163,23 +150,10 @@ const FocusScopeResponder = {
150
props: FocusScopeProps,
151
state: FocusScopeState,
152
): void {
166
- if (
167
- props.restoreFocus &&
168
- state.nodeToRestore !== null &&
169
- context.hasOwnership()
170
- ) {
153
+ if (props.restoreFocus && state.nodeToRestore !== null) {
154
focusElement(state.nodeToRestore);
155
}
156
},
174
- onOwnershipChange(
175
- context: ReactResponderContext,
176
- props: FocusScopeProps,
177
- state: FocusScopeState,
178
- ): void {
179
- if (!context.hasOwnership()) {
180
- state.currentFocusedNode = null;
181
- }
182
- },
157
};
158
159
export default React.unstable_createEventComponent(
packages/react-events/src/Hover.js
-1
@@ -287,7 +287,6 @@ const HoverResponder = {
287
};
288
},
289
allowMultipleHostChildren: false,
290
- stopLocalPropagation: true,
290
onEvent(
291
event: ReactResponderEvent,
292
context: ReactResponderContext,
packages/react-events/src/Press.js
-1
@@ -633,7 +633,6 @@ const PressResponder = {
633
};
634
},
635
allowMultipleHostChildren: false,
636
- stopLocalPropagation: true,
636
onEvent(
637
event: ReactResponderEvent,
638
context: ReactResponderContext,
packages/react-events/src/Scroll.js
-1
@@ -126,7 +126,6 @@ const ScrollResponder = {
126
};
127
},
128
allowMultipleHostChildren: true,
129
- stopLocalPropagation: true,
129
onEvent(
130
event: ReactResponderEvent,
131
context: ReactResponderContext,
packages/react-events/src/Swipe.js
-1
@@ -104,7 +104,6 @@ const SwipeResponder = {
104
};
105
},
106
allowMultipleHostChildren: false,
107
- stopLocalPropagation: true,
107
onEvent(
108
event: ReactResponderEvent,
109
context: ReactResponderContext,
packages/shared/ReactTypes.js
+1
-2
@@ -90,7 +90,6 @@ export type ReactEventResponder = {
90
rootEventTypes?: Array<ReactEventResponderEventType>,
91
createInitialState?: (props: null | Object) => Object,
92
allowMultipleHostChildren: boolean,
93
- stopLocalPropagation: boolean,
93
onEvent?: (
94
event: ReactResponderEvent,
95
context: ReactResponderContext,
@@ -194,7 +193,6 @@ export type ReactResponderContext = {
193
rootEventTypes: Array<ReactEventResponderEventType>,
194
) => void,
195
hasOwnership: () => boolean,
197
- requestResponderOwnership: () => boolean,
196
requestGlobalOwnership: () => boolean,
197
releaseOwnership: () => boolean,
198
setTimeout: (func: () => void, timeout: number) => number,
@@ -209,4 +207,5 @@ export type ReactResponderContext = {
207
elementType: string,
208
deep: boolean,
209
) => boolean,
210
+ continueLocalPropagation(): void,
211
};