Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flare] Update interactiveUpdates flushing heuristics #15687

Merged
merged 14 commits into from May 21, 2019
56 changes: 56 additions & 0 deletions packages/react-events/src/__tests__/Press-test.internal.js
Expand Up @@ -13,6 +13,7 @@ let React;
let ReactFeatureFlags;
let ReactDOM;
let Press;
let Scheduler;

const DEFAULT_LONG_PRESS_DELAY = 500;

Expand Down Expand Up @@ -45,6 +46,7 @@ describe('Event responder: Press', () => {
React = require('react');
ReactDOM = require('react-dom');
Press = require('react-events/press');
Scheduler = require('scheduler');

container = document.createElement('div');
document.body.appendChild(container);
Expand Down Expand Up @@ -2301,4 +2303,58 @@ describe('Event responder: Press', () => {
},
]);
});

it('should properly only flush sync once when the event systems are mixed', () => {
const ref = React.createRef();
let renderCounts = 0;

function dispatchEvent(name) {
const event = createEvent(name);
Object.defineProperty(event, 'timeStamp', {
value: 100,
});
window.event = event;
ref.current.dispatchEvent(event);
window.event = null;
}

function MyComponent() {
const [, updateCounter] = React.useState(0);
renderCounts++;

function handlePress() {
updateCounter(count => count + 1);
}

return (
<div>
<Press onPress={handlePress}>
<button
ref={ref}
onClick={() => {
updateCounter(count => count + 1);
}}>
Press me
</button>
</Press>
</div>
);
}

const newContainer = document.createElement('div');
const root = ReactDOM.unstable_createRoot(newContainer);
document.body.appendChild(newContainer);
root.render(<MyComponent />);
Scheduler.flushAll();

dispatchEvent('pointerdown');
dispatchEvent('pointerup');
dispatchEvent('click');

expect(renderCounts).toBe(2);
Scheduler.flushAll();
expect(renderCounts).toBe(4);

document.body.removeChild(newContainer);
});
trueadm marked this conversation as resolved.
Show resolved Hide resolved
});
28 changes: 21 additions & 7 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Expand Up @@ -26,6 +26,7 @@ import {
disableYielding,
enableSchedulerTracing,
revertPassiveEffectsChange,
enableEventAPI,
} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import invariant from 'shared/invariant';
Expand Down Expand Up @@ -604,20 +605,33 @@ export function deferredUpdates<A>(fn: () => A): A {
return runWithPriority(NormalPriority, fn);
}

let interactiveEventTimeStamp = 0;

export function interactiveUpdates<A, B, C, R>(
fn: (A, B, C) => R,
a: A,
b: B,
c: C,
): R {
if (workPhase === NotWorking) {
// TODO: Remove this call. Instead of doing this automatically, the caller
// should explicitly call flushInteractiveUpdates.
flushPendingDiscreteUpdates();
const currentEvent = typeof window !== 'undefined' && window.event;
trueadm marked this conversation as resolved.
Show resolved Hide resolved
let skipFlushing = false;

if (enableEventAPI && currentEvent) {
if (currentEvent.timeStamp === interactiveEventTimeStamp) {
skipFlushing = true;
}
interactiveEventTimeStamp = currentEvent.timeStamp;
}
if (!revertPassiveEffectsChange) {
// TODO: Remove this call for the same reason as above.
flushPassiveEffects();
if (!skipFlushing) {
if (workPhase === NotWorking) {
// TODO: Remove this call. Instead of doing this automatically, the caller
// should explicitly call flushInteractiveUpdates.
flushPendingDiscreteUpdates();
}
if (!revertPassiveEffectsChange) {
// TODO: Remove this call for the same reason as above.
flushPassiveEffects();
}
}
return runWithPriority(UserBlockingPriority, fn.bind(null, a, b, c));
}
Expand Down