Skip to content

Commit aece811

Browse files
authored
Refactor EventComponent logic + add onOwnershipChange callback (#15354)
1 parent 183d1f4 commit aece811

File tree

13 files changed

+229
-117
lines changed

13 files changed

+229
-117
lines changed

packages/react-art/src/ReactARTHostConfig.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import * as Scheduler from 'scheduler';
1111
import invariant from 'shared/invariant';
1212

1313
import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
14+
import type {ReactEventComponentInstance} from 'shared/ReactTypes';
1415

1516
// Intentionally not named imports because Rollup would
1617
// use dynamic dispatch for CommonJS interop named imports.
@@ -439,17 +440,20 @@ export function unhideTextInstance(textInstance, text): void {
439440
// Noop
440441
}
441442

442-
export function handleEventComponent(
443-
eventResponder: ReactEventResponder,
444-
rootContainerInstance: Container,
443+
export function mountEventComponent(
444+
eventComponentInstance: ReactEventComponentInstance,
445+
) {
446+
throw new Error('Not yet implemented.');
447+
}
448+
449+
export function updateEventComponent(
450+
eventComponentInstance: ReactEventComponentInstance,
445451
) {
446452
throw new Error('Not yet implemented.');
447453
}
448454

449455
export function unmountEventComponent(
450-
eventResponder: ReactEventResponder,
451-
rootContainerInstance: Container,
452-
internalInstanceHandle: Object,
456+
eventComponentInstance: ReactEventComponentInstance,
453457
): void {
454458
throw new Error('Not yet implemented.');
455459
}

packages/react-dom/src/client/ReactDOMHostConfig.js

+20-10
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ import {
4444
import dangerousStyleValue from '../shared/dangerousStyleValue';
4545

4646
import type {DOMContainer} from './ReactDOM';
47-
import type {ReactEventResponder} from 'shared/ReactTypes';
48-
import {unmountEventResponder} from '../events/DOMEventResponderSystem';
47+
import type {ReactEventComponentInstance} from 'shared/ReactTypes';
48+
import {
49+
mountEventResponder,
50+
unmountEventResponder,
51+
} from '../events/DOMEventResponderSystem';
4952
import {REACT_EVENT_TARGET_TOUCH_HIT} from 'shared/ReactSymbols';
5053
import {canUseDOM} from 'shared/ExecutionEnvironment';
5154

@@ -888,27 +891,34 @@ export function didNotFindHydratableSuspenseInstance(
888891
}
889892
}
890893

891-
export function handleEventComponent(
892-
eventResponder: ReactEventResponder,
893-
rootContainerInstance: Container,
894+
export function mountEventComponent(
895+
eventComponentInstance: ReactEventComponentInstance,
894896
): void {
895897
if (enableEventAPI) {
898+
mountEventResponder(eventComponentInstance);
899+
updateEventComponent(eventComponentInstance);
900+
}
901+
}
902+
903+
export function updateEventComponent(
904+
eventComponentInstance: ReactEventComponentInstance,
905+
): void {
906+
if (enableEventAPI) {
907+
const rootContainerInstance = ((eventComponentInstance.rootInstance: any): Container);
896908
const rootElement = rootContainerInstance.ownerDocument;
897909
listenToEventResponderEventTypes(
898-
eventResponder.targetEventTypes,
910+
eventComponentInstance.responder.targetEventTypes,
899911
rootElement,
900912
);
901913
}
902914
}
903915

904916
export function unmountEventComponent(
905-
eventResponder: ReactEventResponder,
906-
rootContainerInstance: Container,
907-
internalInstanceHandle: Object,
917+
eventComponentInstance: ReactEventComponentInstance,
908918
): void {
909919
if (enableEventAPI) {
910920
// TODO stop listening to targetEventTypes
911-
unmountEventResponder(eventResponder, internalInstanceHandle);
921+
unmountEventResponder(eventComponentInstance);
912922
}
913923
}
914924

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

+76-45
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
EventTarget as EventTargetWorkTag,
2323
} from 'shared/ReactWorkTags';
2424
import type {
25-
ReactEventResponder,
2625
ReactEventResponderEventType,
26+
ReactEventComponentInstance,
2727
} from 'shared/ReactTypes';
2828
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
2929
import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
@@ -55,7 +55,7 @@ type PartialEventObject = {
5555
};
5656

5757
let currentOwner = null;
58-
let currentFiber: Fiber;
58+
let currentInstance: ReactEventComponentInstance;
5959
let currentEventQueue: EventQueue;
6060

6161
const eventResponderContext: ResponderContext = {
@@ -140,12 +140,10 @@ const eventResponderContext: ResponderContext = {
140140
return false;
141141
},
142142
isTargetWithinEventComponent(target: Element | Document): boolean {
143-
const eventFiber = currentFiber;
144-
145143
if (target != null) {
146144
let fiber = getClosestInstanceFromNode(target);
147145
while (fiber !== null) {
148-
if (fiber === eventFiber || fiber === eventFiber.alternate) {
146+
if (fiber.stateNode === currentInstance) {
149147
return true;
150148
}
151149
fiber = fiber.return;
@@ -174,78 +172,78 @@ const eventResponderContext: ResponderContext = {
174172
rootEventTypes: Array<ReactEventResponderEventType>,
175173
): void {
176174
listenToResponderEventTypesImpl(rootEventTypes, doc);
177-
const eventComponent = currentFiber;
178175
for (let i = 0; i < rootEventTypes.length; i++) {
179176
const rootEventType = rootEventTypes[i];
180177
const topLevelEventType =
181178
typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
182-
let rootEventComponents = rootEventTypesToEventComponents.get(
179+
let rootEventComponentInstances = rootEventTypesToEventComponentInstances.get(
183180
topLevelEventType,
184181
);
185-
if (rootEventComponents === undefined) {
186-
rootEventComponents = new Set();
187-
rootEventTypesToEventComponents.set(
182+
if (rootEventComponentInstances === undefined) {
183+
rootEventComponentInstances = new Set();
184+
rootEventTypesToEventComponentInstances.set(
188185
topLevelEventType,
189-
rootEventComponents,
186+
rootEventComponentInstances,
190187
);
191188
}
192-
rootEventComponents.add(eventComponent);
189+
rootEventComponentInstances.add(currentInstance);
193190
}
194191
},
195192
removeRootEventTypes(
196193
rootEventTypes: Array<ReactEventResponderEventType>,
197194
): void {
198-
const eventComponent = currentFiber;
199195
for (let i = 0; i < rootEventTypes.length; i++) {
200196
const rootEventType = rootEventTypes[i];
201197
const topLevelEventType =
202198
typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
203-
let rootEventComponents = rootEventTypesToEventComponents.get(
199+
let rootEventComponents = rootEventTypesToEventComponentInstances.get(
204200
topLevelEventType,
205201
);
206202
if (rootEventComponents !== undefined) {
207-
rootEventComponents.delete(eventComponent);
203+
rootEventComponents.delete(currentInstance);
208204
}
209205
}
210206
},
211207
hasOwnership(): boolean {
212-
return currentOwner === currentFiber;
208+
return currentOwner === currentInstance;
213209
},
214210
requestOwnership(): boolean {
215211
if (currentOwner !== null) {
216212
return false;
217213
}
218-
currentOwner = currentFiber;
214+
currentOwner = currentInstance;
215+
triggerOwnershipListeners();
219216
return true;
220217
},
221218
releaseOwnership(): boolean {
222-
if (currentOwner !== currentFiber) {
219+
if (currentOwner !== currentInstance) {
223220
return false;
224221
}
225222
currentOwner = null;
223+
triggerOwnershipListeners();
226224
return false;
227225
},
228226
setTimeout(func: () => void, delay): TimeoutID {
229-
const contextFiber = currentFiber;
227+
const contextInstance = currentInstance;
230228
return setTimeout(() => {
231229
const previousEventQueue = currentEventQueue;
232-
const previousFiber = currentFiber;
230+
const previousInstance = currentInstance;
233231
currentEventQueue = createEventQueue();
234-
currentFiber = contextFiber;
232+
currentInstance = contextInstance;
235233
try {
236234
func();
237235
batchedUpdates(processEventQueue, currentEventQueue);
238236
} finally {
239-
currentFiber = previousFiber;
237+
currentInstance = previousInstance;
240238
currentEventQueue = previousEventQueue;
241239
}
242240
}, delay);
243241
},
244242
};
245243

246-
const rootEventTypesToEventComponents: Map<
244+
const rootEventTypesToEventComponentInstances: Map<
247245
DOMTopLevelEventType | string,
248-
Set<Fiber>,
246+
Set<ReactEventComponentInstance>,
249247
> = new Map();
250248
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
251249
const eventsWithStopPropagation:
@@ -255,6 +253,7 @@ const targetEventTypeCached: Map<
255253
Array<ReactEventResponderEventType>,
256254
Set<DOMTopLevelEventType>,
257255
> = new Map();
256+
const ownershipChangeListeners: Set<ReactEventComponentInstance> = new Set();
258257

259258
function createResponderEvent(
260259
topLevelType: string,
@@ -343,25 +342,24 @@ function getTargetEventTypes(
343342

344343
function handleTopLevelType(
345344
topLevelType: DOMTopLevelEventType,
346-
fiber: Fiber,
347345
responderEvent: ResponderEvent,
346+
eventComponentInstance: ReactEventComponentInstance,
348347
isRootLevelEvent: boolean,
349348
): void {
350-
const responder: ReactEventResponder = fiber.type.responder;
349+
let {props, responder, state} = eventComponentInstance;
351350
if (!isRootLevelEvent) {
352351
// Validate the target event type exists on the responder
353352
const targetEventTypes = getTargetEventTypes(responder.targetEventTypes);
354353
if (!targetEventTypes.has(topLevelType)) {
355354
return;
356355
}
357356
}
358-
let {props, state} = fiber.stateNode;
359-
const previousFiber = currentFiber;
360-
currentFiber = fiber;
357+
const previousInstance = currentInstance;
358+
currentInstance = eventComponentInstance;
361359
try {
362360
responder.onEvent(responderEvent, eventResponderContext, props, state);
363361
} finally {
364-
currentFiber = previousFiber;
362+
currentInstance = previousInstance;
365363
}
366364
}
367365

@@ -384,23 +382,29 @@ export function runResponderEventsInBatch(
384382
// Traverse up the fiber tree till we find event component fibers.
385383
while (node !== null) {
386384
if (node.tag === EventComponent) {
387-
handleTopLevelType(topLevelType, node, responderEvent, false);
385+
const eventComponentInstance = node.stateNode;
386+
handleTopLevelType(
387+
topLevelType,
388+
responderEvent,
389+
eventComponentInstance,
390+
false,
391+
);
388392
}
389393
node = node.return;
390394
}
391395
// Handle root level events
392-
const rootEventComponents = rootEventTypesToEventComponents.get(
396+
const rootEventInstances = rootEventTypesToEventComponentInstances.get(
393397
topLevelType,
394398
);
395-
if (rootEventComponents !== undefined) {
396-
const rootEventComponentFibers = Array.from(rootEventComponents);
399+
if (rootEventInstances !== undefined) {
400+
const rootEventComponentInstances = Array.from(rootEventInstances);
397401

398-
for (let i = 0; i < rootEventComponentFibers.length; i++) {
399-
const rootEventComponentFiber = rootEventComponentFibers[i];
402+
for (let i = 0; i < rootEventComponentInstances.length; i++) {
403+
const rootEventComponentInstance = rootEventComponentInstances[i];
400404
handleTopLevelType(
401405
topLevelType,
402-
rootEventComponentFiber,
403406
responderEvent,
407+
rootEventComponentInstance,
404408
true,
405409
);
406410
}
@@ -409,26 +413,53 @@ export function runResponderEventsInBatch(
409413
}
410414
}
411415

416+
function triggerOwnershipListeners(): void {
417+
const listeningInstances = Array.from(ownershipChangeListeners);
418+
const previousInstance = currentInstance;
419+
for (let i = 0; i < listeningInstances.length; i++) {
420+
const instance = listeningInstances[i];
421+
const {props, responder, state} = instance;
422+
currentInstance = instance;
423+
try {
424+
responder.onOwnershipChange(eventResponderContext, props, state);
425+
} finally {
426+
currentInstance = previousInstance;
427+
}
428+
}
429+
}
430+
431+
export function mountEventResponder(
432+
eventComponentInstance: ReactEventComponentInstance,
433+
) {
434+
const responder = eventComponentInstance.responder;
435+
if (responder.onOwnershipChange !== undefined) {
436+
ownershipChangeListeners.add(eventComponentInstance);
437+
}
438+
}
439+
412440
export function unmountEventResponder(
413-
responder: ReactEventResponder,
414-
fiber: Fiber,
441+
eventComponentInstance: ReactEventComponentInstance,
415442
): void {
443+
const responder = eventComponentInstance.responder;
416444
const onUnmount = responder.onUnmount;
417445
if (onUnmount !== undefined) {
418-
let {props, state} = fiber.stateNode;
446+
let {props, state} = eventComponentInstance;
419447
const previousEventQueue = currentEventQueue;
420-
const previousFiber = currentFiber;
448+
const previousInstance = currentInstance;
421449
currentEventQueue = createEventQueue();
422-
currentFiber = fiber;
450+
currentInstance = eventComponentInstance;
423451
try {
424452
onUnmount(eventResponderContext, props, state);
425453
} finally {
426454
currentEventQueue = previousEventQueue;
427-
currentFiber = previousFiber;
455+
currentInstance = previousInstance;
428456
}
429457
}
430-
if (currentOwner === fiber) {
431-
// TODO fire owner changed callback
458+
if (currentOwner === eventComponentInstance) {
432459
currentOwner = null;
460+
triggerOwnershipListeners();
461+
}
462+
if (responder.onOwnershipChange !== undefined) {
463+
ownershipChangeListeners.delete(eventComponentInstance);
433464
}
434465
}

0 commit comments

Comments
 (0)