mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Attach new listeners to ReactDOMRoot
This commit is contained in:
@@ -14,7 +14,7 @@ import type {
|
||||
KnownReactSyntheticEvent,
|
||||
ReactSyntheticEvent,
|
||||
} from './ReactSyntheticEventType';
|
||||
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
|
||||
|
||||
import {allNativeEvents} from './EventRegistry';
|
||||
import {
|
||||
@@ -44,10 +44,7 @@ import {
|
||||
getEventHandlerListeners,
|
||||
} from '../client/ReactDOMComponentTree';
|
||||
import {COMMENT_NODE, DOCUMENT_NODE} from '../shared/HTMLNodeType';
|
||||
import {
|
||||
batchedUpdates,
|
||||
flushPendingContinuousUpdates,
|
||||
} from './ReactDOMUpdateBatching';
|
||||
import {batchedUpdates} from './ReactDOMUpdateBatching';
|
||||
import getListener from './getListener';
|
||||
import {passiveBrowserEventsSupported} from './checkPassiveEvents';
|
||||
|
||||
@@ -75,10 +72,6 @@ import * as ChangeEventPlugin from './plugins/ChangeEventPlugin';
|
||||
import * as EnterLeaveEventPlugin from './plugins/EnterLeaveEventPlugin';
|
||||
import * as SelectEventPlugin from './plugins/SelectEventPlugin';
|
||||
import * as SimpleEventPlugin from './plugins/SimpleEventPlugin';
|
||||
import {
|
||||
DiscreteEventPriority,
|
||||
getCurrentUpdatePriority,
|
||||
} from 'react-reconciler/src/ReactEventPriorities';
|
||||
|
||||
type DispatchListener = {
|
||||
instance: null | Fiber,
|
||||
@@ -644,18 +637,6 @@ export function dispatchEventForPluginEventSystem(
|
||||
}
|
||||
node = node.return;
|
||||
}
|
||||
|
||||
// Special case: Flush continuous updates before the capture phase of a discrete event.
|
||||
if (
|
||||
eventSystemFlags & IS_CAPTURE_PHASE &&
|
||||
node !== null &&
|
||||
getCurrentUpdatePriority() === DiscreteEventPriority
|
||||
) {
|
||||
const root: ?FiberRoot = node.stateNode;
|
||||
if (root != null) {
|
||||
flushPendingContinuousUpdates(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ import {
|
||||
getSuspenseInstanceFromFiber,
|
||||
} from 'react-reconciler/src/ReactFiberTreeReflection';
|
||||
import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
|
||||
import {flushPendingContinuousUpdates} from './ReactDOMUpdateBatching';
|
||||
import {type EventSystemFlags, IS_CAPTURE_PHASE} from './EventSystemFlags';
|
||||
import {allNativeEvents} from './EventRegistry';
|
||||
|
||||
import getEventTarget from './getEventTarget';
|
||||
import {
|
||||
@@ -111,6 +113,38 @@ export function createEventListenerWrapperWithPriority(
|
||||
);
|
||||
}
|
||||
|
||||
const listeningMarker =
|
||||
'_reactListening' +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.slice(2);
|
||||
|
||||
// For flushing continuous events before a discrete event in capture phase
|
||||
// this function needs to be called before `listenToAllSupportedEvents` so
|
||||
// that it runs before event system's capture phase callback.
|
||||
export function listenToCapturePhaseDiscreteEvents(
|
||||
rootContainerElement: EventTarget,
|
||||
root: FiberRoot,
|
||||
) {
|
||||
if ((rootContainerElement: any)[listeningMarker]) {
|
||||
return;
|
||||
}
|
||||
(rootContainerElement: any)[listeningMarker] = true;
|
||||
|
||||
function onEvent() {
|
||||
flushPendingContinuousUpdates(root);
|
||||
}
|
||||
allNativeEvents.forEach(domEventName => {
|
||||
const eventPriority = getEventPriority(domEventName);
|
||||
if (eventPriority !== DiscreteEventPriority) {
|
||||
return;
|
||||
}
|
||||
rootContainerElement.addEventListener(domEventName, onEvent, {
|
||||
capture: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchDiscreteEvent(
|
||||
domEventName: DOMEventName,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
|
||||
@@ -61,6 +61,7 @@ import {
|
||||
unmarkContainerAsRoot,
|
||||
} from 'react-dom-bindings/src/client/ReactDOMComponentTree';
|
||||
import {listenToAllSupportedEvents} from 'react-dom-bindings/src/events/DOMPluginEventSystem';
|
||||
import {listenToCapturePhaseDiscreteEvents} from 'react-dom-bindings/src/events/ReactDOMEventLIstener';
|
||||
import {
|
||||
ELEMENT_NODE,
|
||||
COMMENT_NODE,
|
||||
@@ -256,6 +257,7 @@ export function createRoot(
|
||||
container.nodeType === COMMENT_NODE
|
||||
? (container.parentNode: any)
|
||||
: container;
|
||||
listenToCapturePhaseDiscreteEvents(rootContainerElement, root);
|
||||
listenToAllSupportedEvents(rootContainerElement);
|
||||
|
||||
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
|
||||
@@ -344,6 +346,7 @@ export function hydrateRoot(
|
||||
Dispatcher.current = ReactDOMClientDispatcher;
|
||||
}
|
||||
// This can't be a comment node since hydration doesn't work on comment nodes anyway.
|
||||
listenToCapturePhaseDiscreteEvents(container, root);
|
||||
listenToAllSupportedEvents(container);
|
||||
|
||||
if (mutableSources) {
|
||||
|
||||
@@ -1287,18 +1287,16 @@ describe('DOMPluginEventSystem', () => {
|
||||
const buttonElement = buttonRef.current;
|
||||
|
||||
// Expect the click event to be able to get the latest state value set by mouse over events
|
||||
await act(async () => {
|
||||
buttonElement.dispatchEvent(
|
||||
new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
capture: true,
|
||||
cancelable: true,
|
||||
relatedTarget: null,
|
||||
clientX: 5,
|
||||
}),
|
||||
);
|
||||
dispatchClickEvent(buttonElement);
|
||||
});
|
||||
buttonElement.dispatchEvent(
|
||||
new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
capture: true,
|
||||
cancelable: true,
|
||||
relatedTarget: null,
|
||||
clientX: 5,
|
||||
}),
|
||||
);
|
||||
dispatchClickEvent(buttonElement);
|
||||
expect(Scheduler).toHaveYielded(['Render:5', 'Click:5']);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user