Skip to content

Commit 539640d

Browse files
authored
[react-events] Various core tweaks for event responder system (#16654)
1 parent af03276 commit 539640d

File tree

17 files changed

+181
-89
lines changed

17 files changed

+181
-89
lines changed

packages/react-dom/src/events/DOMEventResponderSystem.js

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
PASSIVE_NOT_SUPPORTED,
1313
} from 'legacy-events/EventSystemFlags';
1414
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
15-
import {HostComponent} from 'shared/ReactWorkTags';
15+
import {HostComponent, ScopeComponent} from 'shared/ReactWorkTags';
1616
import type {EventPriority} from 'shared/ReactTypes';
1717
import type {
1818
ReactDOMEventResponder,
@@ -66,6 +66,7 @@ type ResponderTimer = {|
6666
instance: ReactDOMEventResponderInstance,
6767
func: () => void,
6868
id: number,
69+
targetFiber: Fiber | null,
6970
timeStamp: number,
7071
|};
7172

@@ -80,6 +81,7 @@ let currentTimers = new Map();
8081
let currentInstance: null | ReactDOMEventResponderInstance = null;
8182
let currentTimerIDCounter = 0;
8283
let currentDocument: null | Document = null;
84+
let currentTargetFiber: null | Fiber = null;
8385

8486
const eventResponderContext: ReactDOMResponderContext = {
8587
dispatchEvent(
@@ -158,16 +160,20 @@ const eventResponderContext: ReactDOMResponderContext = {
158160
validateResponderContext();
159161
const childFiber = getClosestInstanceFromNode(childTarget);
160162
const parentFiber = getClosestInstanceFromNode(parentTarget);
161-
const parentAlternateFiber = parentFiber.alternate;
162163

163-
let node = childFiber;
164-
while (node !== null) {
165-
if (node === parentFiber || node === parentAlternateFiber) {
166-
return true;
164+
if (childFiber != null && parentFiber != null) {
165+
const parentAlternateFiber = parentFiber.alternate;
166+
let node = childFiber;
167+
while (node !== null) {
168+
if (node === parentFiber || node === parentAlternateFiber) {
169+
return true;
170+
}
171+
node = node.return;
167172
}
168-
node = node.return;
173+
return false;
169174
}
170-
return false;
175+
// Fallback to DOM APIs
176+
return parentTarget.contains(childTarget);
171177
},
172178
addRootEventTypes(rootEventTypes: Array<string>): void {
173179
validateResponderContext();
@@ -221,6 +227,7 @@ const eventResponderContext: ReactDOMResponderContext = {
221227
instance: ((currentInstance: any): ReactDOMEventResponderInstance),
222228
func,
223229
id: timerId,
230+
targetFiber: currentTargetFiber,
224231
timeStamp: currentTimeStamp,
225232
});
226233
activeTimeouts.set(timerId, timeout);
@@ -260,6 +267,24 @@ const eventResponderContext: ReactDOMResponderContext = {
260267
return false;
261268
},
262269
enqueueStateRestore,
270+
getCurrentTarget(): Element | null {
271+
validateResponderContext();
272+
const responderFiber = ((currentInstance: any): ReactDOMEventResponderInstance)
273+
.fiber;
274+
let fiber = currentTargetFiber;
275+
let currentTarget = null;
276+
277+
while (fiber !== null) {
278+
if (fiber.tag === HostComponent) {
279+
currentTarget = fiber.stateNode;
280+
}
281+
if (fiber === responderFiber || fiber.alternate === responderFiber) {
282+
break;
283+
}
284+
fiber = fiber.return;
285+
}
286+
return currentTarget;
287+
},
263288
};
264289

265290
function validateEventValue(eventValue: any): void {
@@ -317,7 +342,8 @@ function doesFiberHaveResponder(
317342
fiber: Fiber,
318343
responder: ReactDOMEventResponder,
319344
): boolean {
320-
if (fiber.tag === HostComponent) {
345+
const tag = fiber.tag;
346+
if (tag === HostComponent || tag === ScopeComponent) {
321347
const dependencies = fiber.dependencies;
322348
if (dependencies !== null) {
323349
const respondersMap = dependencies.responders;
@@ -341,8 +367,9 @@ function processTimers(
341367
try {
342368
batchedEventUpdates(() => {
343369
for (let i = 0; i < timersArr.length; i++) {
344-
const {instance, func, id, timeStamp} = timersArr[i];
370+
const {instance, func, id, timeStamp, targetFiber} = timersArr[i];
345371
currentInstance = instance;
372+
currentTargetFiber = targetFiber;
346373
currentTimeStamp = timeStamp + delay;
347374
try {
348375
func();
@@ -355,6 +382,7 @@ function processTimers(
355382
currentTimers = null;
356383
currentInstance = null;
357384
currentTimeStamp = 0;
385+
currentTargetFiber = null;
358386
}
359387
}
360388

@@ -386,7 +414,6 @@ function createDOMResponderEvent(
386414
passiveSupported,
387415
pointerId,
388416
pointerType: eventPointerType,
389-
responderTarget: null,
390417
target: nativeEventTarget,
391418
type: topLevelType,
392419
};
@@ -443,13 +470,16 @@ function traverseAndHandleEventResponderInstances(
443470
let node = targetFiber;
444471
while (node !== null) {
445472
const {dependencies, tag} = node;
446-
if (tag === HostComponent && dependencies !== null) {
473+
if (
474+
(tag === HostComponent || tag === ScopeComponent) &&
475+
dependencies !== null
476+
) {
447477
const respondersMap = dependencies.responders;
448478
if (respondersMap !== null) {
449479
const responderInstances = Array.from(respondersMap.values());
450480
for (let i = 0, length = responderInstances.length; i < length; i++) {
451481
const responderInstance = responderInstances[i];
452-
const {props, responder, state, target} = responderInstance;
482+
const {props, responder, state} = responderInstance;
453483
if (
454484
!visitedResponders.has(responder) &&
455485
validateResponderTargetEventTypes(eventType, responder)
@@ -458,9 +488,6 @@ function traverseAndHandleEventResponderInstances(
458488
const onEvent = responder.onEvent;
459489
if (onEvent !== null) {
460490
currentInstance = responderInstance;
461-
responderEvent.responderTarget = ((target: any):
462-
| Element
463-
| Document);
464491
onEvent(responderEvent, eventResponderContext, props, state);
465492
}
466493
}
@@ -478,11 +505,10 @@ function traverseAndHandleEventResponderInstances(
478505

479506
for (let i = 0; i < responderInstances.length; i++) {
480507
const responderInstance = responderInstances[i];
481-
const {props, responder, state, target} = responderInstance;
508+
const {props, responder, state} = responderInstance;
482509
const onRootEvent = responder.onRootEvent;
483510
if (onRootEvent !== null) {
484511
currentInstance = responderInstance;
485-
responderEvent.responderTarget = ((target: any): Element | Document);
486512
onRootEvent(responderEvent, eventResponderContext, props, state);
487513
}
488514
}
@@ -562,7 +588,9 @@ export function dispatchEventForResponderEventSystem(
562588
const previousTimers = currentTimers;
563589
const previousTimeStamp = currentTimeStamp;
564590
const previousDocument = currentDocument;
591+
const previousTargetFiber = currentTargetFiber;
565592
currentTimers = null;
593+
currentTargetFiber = targetFiber;
566594
// nodeType 9 is DOCUMENT_NODE
567595
currentDocument =
568596
(nativeEventTarget: any).nodeType === 9
@@ -585,6 +613,7 @@ export function dispatchEventForResponderEventSystem(
585613
currentInstance = previousInstance;
586614
currentTimeStamp = previousTimeStamp;
587615
currentDocument = previousDocument;
616+
currentTargetFiber = previousTargetFiber;
588617
}
589618
}
590619
}

packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ describe('DOMEventResponderSystem', () => {
10171017
const obj = {
10181018
counter,
10191019
timeStamp: context.getTimeStamp(),
1020-
target: event.responderTarget,
1020+
target: context.getCurrentTarget(),
10211021
type: 'click-test',
10221022
};
10231023
context.dispatchEvent(obj, props.onClick, DiscreteEvent);

packages/react-events/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ type ResponderEventType = string;
2222

2323
type ResponderEvent = {|
2424
nativeEvent: any,
25-
responderTarget: Element | Document,
2625
target: Element | Document,
2726
pointerType: string,
2827
type: string,

packages/react-events/src/dom/Focus.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ const focusResponderImpl = {
289289

290290
switch (type) {
291291
case 'focus': {
292-
state.focusTarget = event.responderTarget;
292+
state.focusTarget = context.getCurrentTarget();
293293
// Limit focus events to the direct child of the event component.
294294
// Browser focus is not expected to bubble.
295295
if (!state.isFocused && state.focusTarget === target) {
@@ -427,7 +427,7 @@ const focusWithinResponderImpl = {
427427

428428
switch (type) {
429429
case 'focus': {
430-
state.focusTarget = event.responderTarget;
430+
state.focusTarget = context.getCurrentTarget();
431431
// Limit focus events to the direct child of the event component.
432432
// Browser focus is not expected to bubble.
433433
if (!state.isFocused) {

packages/react-events/src/dom/Hover.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ const hoverResponderImpl = {
235235
// START
236236
case 'pointerover': {
237237
if (!state.isHovered && pointerType !== 'touch') {
238-
state.hoverTarget = event.responderTarget;
238+
state.hoverTarget = context.getCurrentTarget();
239239
dispatchHoverStartEvents(event, context, props, state);
240240
}
241241
break;
@@ -295,7 +295,7 @@ const hoverResponderFallbackImpl = {
295295
// START
296296
case 'mouseover': {
297297
if (!state.isHovered && !state.ignoreEmulatedMouseEvents) {
298-
state.hoverTarget = event.responderTarget;
298+
state.hoverTarget = context.getCurrentTarget();
299299
dispatchHoverStartEvents(event, context, props, state);
300300
}
301301
break;

packages/react-events/src/dom/Input.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,30 +179,31 @@ const inputResponderImpl = {
179179
context: ReactDOMResponderContext,
180180
props: InputResponderProps,
181181
): void {
182-
const {responderTarget, type, target} = event;
182+
const {type, target} = event;
183183

184184
if (props.disabled) {
185185
return;
186186
}
187-
if (target !== responderTarget || responderTarget === null) {
187+
const currentTarget = context.getCurrentTarget();
188+
if (target !== currentTarget || currentTarget === null) {
188189
return;
189190
}
190191
switch (type) {
191192
default: {
192193
if (shouldUseChangeEvent(target) && type === 'change') {
193-
dispatchBothChangeEvents(event, context, props, responderTarget);
194+
dispatchBothChangeEvents(event, context, props, currentTarget);
194195
} else if (
195196
isTextInputElement(target) &&
196197
(type === 'input' || type === 'change') &&
197198
updateValueIfChanged(target)
198199
) {
199-
dispatchBothChangeEvents(event, context, props, responderTarget);
200+
dispatchBothChangeEvents(event, context, props, currentTarget);
200201
} else if (
201202
isCheckable(target) &&
202203
type === 'click' &&
203204
updateValueIfChanged(target)
204205
) {
205-
dispatchBothChangeEvents(event, context, props, responderTarget);
206+
dispatchBothChangeEvents(event, context, props, currentTarget);
206207
}
207208
break;
208209
}

packages/react-events/src/dom/Keyboard.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ function createKeyboardEvent(
131131
event: ReactDOMResponderEvent,
132132
context: ReactDOMResponderContext,
133133
type: KeyboardEventType,
134-
target: Document | Element,
135134
defaultPrevented: boolean,
136135
): KeyboardEvent {
137136
const nativeEvent = (event: any).nativeEvent;
@@ -144,6 +143,7 @@ function createKeyboardEvent(
144143
repeat,
145144
shiftKey,
146145
} = nativeEvent;
146+
const target = ((context.getCurrentTarget(): any): Element);
147147

148148
return {
149149
altKey,
@@ -166,14 +166,12 @@ function dispatchKeyboardEvent(
166166
listener: KeyboardEvent => void,
167167
context: ReactDOMResponderContext,
168168
type: KeyboardEventType,
169-
target: Element | Document,
170169
defaultPrevented: boolean,
171170
): void {
172171
const syntheticEvent = createKeyboardEvent(
173172
event,
174173
context,
175174
type,
176-
target,
177175
defaultPrevented,
178176
);
179177
context.dispatchEvent(syntheticEvent, listener, DiscreteEvent);
@@ -186,7 +184,7 @@ const keyboardResponderImpl = {
186184
context: ReactDOMResponderContext,
187185
props: KeyboardProps,
188186
): void {
189-
const {responderTarget, type} = event;
187+
const {type} = event;
190188
const nativeEvent: any = event.nativeEvent;
191189

192190
if (props.disabled) {
@@ -227,7 +225,6 @@ const keyboardResponderImpl = {
227225
onKeyDown,
228226
context,
229227
'keydown',
230-
((responderTarget: any): Element | Document),
231228
defaultPrevented,
232229
);
233230
}
@@ -239,7 +236,6 @@ const keyboardResponderImpl = {
239236
onKeyUp,
240237
context,
241238
'keyup',
242-
((responderTarget: any): Element | Document),
243239
defaultPrevented,
244240
);
245241
}

packages/react-events/src/dom/Press.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ const pressResponderImpl = {
582582
// We set these here, before the button check so we have this
583583
// data around for handling of the context menu
584584
state.pointerType = pointerType;
585-
const pressTarget = (state.pressTarget = event.responderTarget);
585+
const pressTarget = (state.pressTarget = context.getCurrentTarget());
586586
if (isPointerEvent) {
587587
state.activePointerId = pointerId;
588588
} else if (isTouchEvent) {
@@ -634,7 +634,7 @@ const pressResponderImpl = {
634634

635635
if (isFunction(onPress) && isScreenReaderVirtualClick(nativeEvent)) {
636636
state.pointerType = 'keyboard';
637-
state.pressTarget = event.responderTarget;
637+
state.pressTarget = context.getCurrentTarget();
638638
const preventDefault = props.preventDefault;
639639

640640
if (preventDefault !== false) {

packages/react-events/src/rn/Press.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ const pressResponderImpl = {
412412
if (type === 'topTouchStart') {
413413
if (!state.isPressed) {
414414
state.pointerType = 'touch';
415-
const pressTarget = (state.pressTarget = event.responderTarget);
415+
const pressTarget = (state.pressTarget = context.getCurrentTarget());
416416
const touchEvent = getTouchFromPressEvent(nativeEvent);
417417
if (touchEvent === null) {
418418
return;

0 commit comments

Comments
 (0)