Compare commits
3 Commits
v19.0.3
...
ty-flush-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2cebbf9ee | ||
|
|
31ca11900f | ||
|
|
7c8acf9d26 |
@@ -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,
|
||||
|
||||
@@ -24,6 +24,7 @@ let discreteUpdatesImpl = function(fn, a, b, c, d) {
|
||||
return fn(a, b, c, d);
|
||||
};
|
||||
let flushSyncImpl = function() {};
|
||||
let flushPendingContinuousUpdatesImpl = function() {};
|
||||
|
||||
let isInsideEventHandler = false;
|
||||
|
||||
@@ -64,12 +65,18 @@ export function discreteUpdates(fn, a, b, c, d) {
|
||||
return discreteUpdatesImpl(fn, a, b, c, d);
|
||||
}
|
||||
|
||||
export function flushPendingContinuousUpdates(root) {
|
||||
return flushPendingContinuousUpdatesImpl(root);
|
||||
}
|
||||
|
||||
export function setBatchingImplementation(
|
||||
_batchedUpdatesImpl,
|
||||
_discreteUpdatesImpl,
|
||||
_flushSyncImpl,
|
||||
_flushPendingContinuousUpdatesImpl,
|
||||
) {
|
||||
batchedUpdatesImpl = _batchedUpdatesImpl;
|
||||
discreteUpdatesImpl = _discreteUpdatesImpl;
|
||||
flushSyncImpl = _flushSyncImpl;
|
||||
flushPendingContinuousUpdatesImpl = _flushPendingContinuousUpdatesImpl;
|
||||
}
|
||||
|
||||
2
packages/react-dom/src/client/ReactDOM.js
vendored
2
packages/react-dom/src/client/ReactDOM.js
vendored
@@ -35,6 +35,7 @@ import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHand
|
||||
import {
|
||||
batchedUpdates,
|
||||
discreteUpdates,
|
||||
flushPendingContinuousUpdates,
|
||||
flushSync as flushSyncWithoutWarningIfAlreadyRendering,
|
||||
isAlreadyRendering,
|
||||
flushControlled,
|
||||
@@ -108,6 +109,7 @@ setBatchingImplementation(
|
||||
batchedUpdates,
|
||||
discreteUpdates,
|
||||
flushSyncWithoutWarningIfAlreadyRendering,
|
||||
flushPendingContinuousUpdates,
|
||||
);
|
||||
|
||||
function createPortal(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1260,6 +1260,46 @@ describe('DOMPluginEventSystem', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('flushes continuous events in the capture phase of a discrete event', async () => {
|
||||
const buttonRef = React.createRef();
|
||||
function Test() {
|
||||
const [clientX, setClientX] = React.useState(0);
|
||||
Scheduler.unstable_yieldValue(`Render:${clientX}`);
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
onClickCapture={() => {
|
||||
Scheduler.unstable_yieldValue(`Click:${clientX}`);
|
||||
}}
|
||||
onMouseOver={e => {
|
||||
setClientX(e.clientX);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
await act(async () => {
|
||||
root.render(<Test />);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded(['Render:0']);
|
||||
const buttonElement = buttonRef.current;
|
||||
|
||||
// Expect the click event to be able to get the latest state value set by mouse over events
|
||||
buttonElement.dispatchEvent(
|
||||
new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
capture: true,
|
||||
cancelable: true,
|
||||
relatedTarget: null,
|
||||
clientX: 5,
|
||||
}),
|
||||
);
|
||||
dispatchClickEvent(buttonElement);
|
||||
expect(Scheduler).toHaveYielded(['Render:5', 'Click:5']);
|
||||
});
|
||||
|
||||
describe('ReactDOM.createEventHandle', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
@@ -61,6 +61,7 @@ import {
|
||||
scheduleUpdateOnFiber,
|
||||
scheduleInitialHydrationOnRoot,
|
||||
flushRoot,
|
||||
flushPendingContinuousUpdates,
|
||||
batchedUpdates,
|
||||
flushSync,
|
||||
isAlreadyRendering,
|
||||
@@ -393,6 +394,7 @@ export {
|
||||
deferredUpdates,
|
||||
discreteUpdates,
|
||||
flushControlled,
|
||||
flushPendingContinuousUpdates,
|
||||
flushSync,
|
||||
isAlreadyRendering,
|
||||
flushPassiveEffects,
|
||||
|
||||
@@ -134,10 +134,12 @@ import {
|
||||
NoLanes,
|
||||
NoLane,
|
||||
SyncLane,
|
||||
InputContinuousLane,
|
||||
NoTimestamp,
|
||||
claimNextTransitionLane,
|
||||
claimNextRetryLane,
|
||||
includesSyncLane,
|
||||
includesSomeLane,
|
||||
isSubsetOfLanes,
|
||||
mergeLanes,
|
||||
removeLanes,
|
||||
@@ -1542,6 +1544,12 @@ export function flushRoot(root: FiberRoot, lanes: Lanes) {
|
||||
}
|
||||
}
|
||||
|
||||
export function flushPendingContinuousUpdates(root: FiberRoot) {
|
||||
if (includesSomeLane(root.pendingLanes, InputContinuousLane)) {
|
||||
flushRoot(root, InputContinuousLane);
|
||||
}
|
||||
}
|
||||
|
||||
export function getExecutionContext(): ExecutionContext {
|
||||
return executionContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user