@samitouri / QOS-React-2 / commits / 97fce318a6

Experiment: Infer the current event priority from the native event (#20748)

* Add the feature flag * Add a host config method * Wire it up to the work loop * Export constants for third-party renderers * Document for third-party renderers

Dan Abramov committed Feb 9, 2021 at 18:32 UTC 97fce318a6618bc883a8578f58b9083484c9e6d7
24 files changed +179 -11
packages/react-art/src/ReactARTHostConfig.js
+12
@@ -7,10 +7,18 @@
7
8 import Transform from 'art/core/transform';
9 import Mode from 'art/modes/current';
10 +import {enableNewReconciler} from 'shared/ReactFeatureFlags';
11 import invariant from 'shared/invariant';
12
13 import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
14
15 +import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
16 +import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
17 +
18 +const DefaultLanePriority = enableNewReconciler
19 + ? DefaultLanePriority_new
20 + : DefaultLanePriority_old;
21 +
22 const pooledTransform = new Transform();
23
24 const NO_CONTEXT = {};
@@ -340,6 +348,10 @@ export function shouldSetTextContent(type, props) {
348 );
349 }
350
351 +export function getCurrentEventPriority() {
352 + return DefaultLanePriority;
353 +}
354 +
355 // The ART renderer is secondary to the React DOM renderer.
356 export const isPrimaryRenderer = false;
357
packages/react-dom/src/client/ReactDOMHostConfig.js
+17
@@ -47,6 +47,7 @@ import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting';
47 import {
48 isEnabled as ReactBrowserEventEmitterIsEnabled,
49 setEnabled as ReactBrowserEventEmitterSetEnabled,
50 + getEventPriority,
51 } from '../events/ReactDOMEventListener';
52 import {getChildNamespace} from '../shared/DOMNamespaces';
53 import {
@@ -65,10 +66,18 @@ import {
66 enableSuspenseServerRenderer,
67 enableCreateEventHandleAPI,
68 enableScopeAPI,
69 + enableNewReconciler,
70 } from 'shared/ReactFeatureFlags';
71 import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
72 import {listenToAllSupportedEvents} from '../events/DOMPluginEventSystem';
73
74 +import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
75 +import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
76 +
77 +const DefaultLanePriority = enableNewReconciler
78 + ? DefaultLanePriority_new
79 + : DefaultLanePriority_old;
80 +
81 export type Type = string;
82 export type Props = {
83 autoFocus?: boolean,
@@ -372,6 +381,14 @@ export function createTextInstance(
381 return textNode;
382 }
383
384 +export function getCurrentEventPriority(): * {
385 + const currentEvent = window.event;
386 + if (currentEvent === undefined) {
387 + return DefaultLanePriority;
388 + }
389 + return getEventPriority(currentEvent.type);
390 +}
391 +
392 export const isPrimaryRenderer = true;
393 export const warnsIfNotActing = true;
394 // This initialization code may run even on server environments
packages/react-dom/src/events/ReactDOMEventListener.js
+1 -1
@@ -348,7 +348,7 @@ export function attemptToDispatchEvent(
348 return null;
349 }
350
351 -function getEventPriority(domEventName: DOMEventName) {
351 +export function getEventPriority(domEventName: DOMEventName): * {
352 switch (domEventName) {
353 // Used by SimpleEventPlugin:
354 case 'cancel':
packages/react-native-renderer/src/ReactFabricHostConfig.js
+12
@@ -21,10 +21,18 @@ import type {
21 import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
22 import {create, diff} from './ReactNativeAttributePayload';
23
24 +import {enableNewReconciler} from 'shared/ReactFeatureFlags';
25 import invariant from 'shared/invariant';
26
27 import {dispatchEvent} from './ReactFabricEventEmitter';
28
29 +import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
30 +import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
31 +
32 +const DefaultLanePriority = enableNewReconciler
33 + ? DefaultLanePriority_new
34 + : DefaultLanePriority_old;
35 +
36 // Modules provided by RN:
37 import {
38 ReactNativeViewConfigRegistry,
@@ -339,6 +347,10 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
347 return false;
348 }
349
350 +export function getCurrentEventPriority(): * {
351 + return DefaultLanePriority;
352 +}
353 +
354 // The Fabric renderer is secondary to the existing React Native renderer.
355 export const isPrimaryRenderer = false;
356
packages/react-native-renderer/src/ReactNativeHostConfig.js
+12
@@ -10,6 +10,7 @@
10 import type {TouchedViewDataAtPoint} from './ReactNativeTypes';
11
12 import invariant from 'shared/invariant';
13 +import {enableNewReconciler} from 'shared/ReactFeatureFlags';
14
15 // Modules provided by RN:
16 import {
@@ -26,6 +27,13 @@ import {
27 } from './ReactNativeComponentTree';
28 import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
29
30 +import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
31 +import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
32 +
33 +const DefaultLanePriority = enableNewReconciler
34 + ? DefaultLanePriority_new
35 + : DefaultLanePriority_old;
36 +
37 const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
38
39 export type Type = string;
@@ -261,6 +269,10 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
269 return false;
270 }
271
272 +export function getCurrentEventPriority(): * {
273 + return DefaultLanePriority;
274 +}
275 +
276 // -------------------
277 // Mutation
278 // -------------------
packages/react-noop-renderer/src/createReactNoop.js
+4
@@ -391,6 +391,10 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
391
392 resetAfterCommit(): void {},
393
394 + getCurrentEventPriority() {
395 + return NoopRenderer.DefaultEventPriority;
396 + },
397 +
398 now: Scheduler.unstable_now,
399
400 isPrimaryRenderer: true,
packages/react-reconciler/README.md
+26
@@ -211,6 +211,32 @@ You can proxy this to `queueMicrotask` or its equivalent in your environment.
211
212 This is a property (not a function) that should be set to `true` if your renderer is the main one on the page. For example, if you're writing a renderer for the Terminal, it makes sense to set it to `true`, but if your renderer is used *on top of* React DOM or some other existing renderer, set it to `false`.
213
214 +#### `getCurrentEventPriority`
215 +
216 +To implement this method, you'll need some constants available on the _returned_ `Renderer` object:
217 +
218 +```js
219 +const HostConfig = {
220 + // ...
221 + getCurrentEventPriority() {
222 + return MyRenderer.DefaultEventPriority;
223 + },
224 + // ...
225 +}
226 +
227 +const MyRenderer = Reconciler(HostConfig);
228 +```
229 +
230 +The constant you return depends on which event, if any, is being handled right now. (In the browser, you can check this using `window.event && window.event.type`).
231 +
232 +* **Discrete events:** If the active event is _directly caused by the user_ (such as mouse and keyboard events) and _each event in a sequence is intentional_ (e.g. `click`), return `MyRenderer.DiscreteEventPriority`. This tells React that they should interrupt any background work and cannot be batched across time.
233 +
234 +* **Continuous events:** If the active event is _directly caused by the user_ but _the user can't distinguish between individual events in a sequence_ (e.g. `mouseover`), return `MyRenderer.ContinuousEventPriority`. This tells React they should interrupt any background work but can be batched across time.
235 +
236 +* **Other events / No active event:** In all other cases, return `MyRenderer.DefaultEventPriority`. This tells React that this event is considered background work, and interactive events will be prioritized over it.
237 +
238 +You can consult the `getCurrentEventPriority()` implementation in `ReactDOMHostConfig.js` for a reference implementation.
239 +
240 ### Mutation Methods
241
242 If you're using React in mutation mode (you probably do), you'll need to implement a few more methods.
packages/react-reconciler/src/ReactFiberReconciler.js
+15
@@ -52,6 +52,9 @@ import {
52 registerMutableSourceForHydration as registerMutableSourceForHydration_old,
53 runWithPriority as runWithPriority_old,
54 getCurrentUpdateLanePriority as getCurrentUpdateLanePriority_old,
55 + DefaultEventPriority as DefaultEventPriority_old,
56 + DiscreteEventPriority as DiscreteEventPriority_old,
57 + ContinuousEventPriority as ContinuousEventPriority_old,
58 } from './ReactFiberReconciler.old';
59
60 import {
@@ -92,6 +95,9 @@ import {
95 registerMutableSourceForHydration as registerMutableSourceForHydration_new,
96 runWithPriority as runWithPriority_new,
97 getCurrentUpdateLanePriority as getCurrentUpdateLanePriority_new,
98 + DefaultEventPriority as DefaultEventPriority_new,
99 + DiscreteEventPriority as DiscreteEventPriority_new,
100 + ContinuousEventPriority as ContinuousEventPriority_new,
101 } from './ReactFiberReconciler.new';
102
103 export const createContainer = enableNewReconciler
@@ -168,6 +174,15 @@ export const createPortal = enableNewReconciler
174 export const createComponentSelector = enableNewReconciler
175 ? createComponentSelector_new
176 : createComponentSelector_old;
177 +export const DefaultEventPriority = enableNewReconciler
178 + ? DefaultEventPriority_new
179 + : DefaultEventPriority_old;
180 +export const DiscreteEventPriority = enableNewReconciler
181 + ? DiscreteEventPriority_new
182 + : DiscreteEventPriority_old;
183 +export const ContinuousEventPriority = enableNewReconciler
184 + ? ContinuousEventPriority_new
185 + : ContinuousEventPriority_old;
186
187 //TODO: "psuedo" is spelled "pseudo"
188 export const createHasPsuedoClassSelector = enableNewReconciler
packages/react-reconciler/src/ReactFiberReconciler.new.js
+9
@@ -94,6 +94,15 @@ import {
94 } from './ReactFiberHotReloading.new';
95 import {markRenderScheduled} from './SchedulingProfiler';
96
97 +// Ideally host configs would import these constants from the reconciler
98 +// entry point, but we can't do this because of a circular dependency.
99 +// They are used by third-party renderers so they need to stay up to date.
100 +export {
101 + InputDiscreteLanePriority as DiscreteEventPriority,
102 + InputContinuousLanePriority as ContinuousEventPriority,
103 + DefaultLanePriority as DefaultEventPriority,
104 +} from './ReactFiberLane.new';
105 +
106 export {registerMutableSourceForHydration} from './ReactMutableSource.new';
107 export {createPortal} from './ReactPortal';
108 export {
packages/react-reconciler/src/ReactFiberReconciler.old.js
+9
@@ -94,6 +94,15 @@ import {
94 } from './ReactFiberHotReloading.old';
95 import {markRenderScheduled} from './SchedulingProfiler';
96
97 +// Ideally host configs would import these constants from the reconciler
98 +// entry point, but we can't do this because of a circular dependency.
99 +// They are used by third-party renderers so they need to stay up to date.
100 +export {
101 + InputDiscreteLanePriority as DiscreteEventPriority,
102 + InputContinuousLanePriority as ContinuousEventPriority,
103 + DefaultLanePriority as DefaultEventPriority,
104 +} from './ReactFiberLane.old';
105 +
106 export {registerMutableSourceForHydration} from './ReactMutableSource.new';
107 export {createPortal} from './ReactPortal';
108 export {
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+19 -5
@@ -35,6 +35,7 @@ import {
35 enableDoubleInvokingEffects,
36 skipUnmountedBoundaries,
37 enableTransitionEntanglement,
38 + enableNativeEventPriorityInference,
39 } from 'shared/ReactFeatureFlags';
40 import ReactSharedInternals from 'shared/ReactSharedInternals';
41 import invariant from 'shared/invariant';
@@ -94,6 +95,7 @@ import {
95 afterActiveInstanceBlur,
96 clearContainer,
97 scheduleMicrotask,
98 + getCurrentEventPriority,
99 } from './ReactFiberHostConfig';
100
101 import {
@@ -461,11 +463,23 @@ export function requestUpdateLane(fiber: Fiber): Lane {
463 const currentLanePriority = getCurrentUpdateLanePriority();
464 lane = findUpdateLane(currentLanePriority, currentEventWipLanes);
465 } else {
464 - const schedulerLanePriority = schedulerPriorityToLanePriority(
465 - schedulerPriority,
466 - );
467 -
468 - lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
466 + if (enableNativeEventPriorityInference) {
467 + const eventLanePriority = getCurrentEventPriority();
468 + if (eventLanePriority === DefaultLanePriority) {
469 + // TODO: move this case into the ReactDOM host config.
470 + const schedulerLanePriority = schedulerPriorityToLanePriority(
471 + schedulerPriority,
472 + );
473 + lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
474 + } else {
475 + lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
476 + }
477 + } else {
478 + const schedulerLanePriority = schedulerPriorityToLanePriority(
479 + schedulerPriority,
480 + );
481 + lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
482 + }
483 }
484
485 return lane;
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+19 -5
@@ -35,6 +35,7 @@ import {
35 enableDoubleInvokingEffects,
36 skipUnmountedBoundaries,
37 enableTransitionEntanglement,
38 + enableNativeEventPriorityInference,
39 } from 'shared/ReactFeatureFlags';
40 import ReactSharedInternals from 'shared/ReactSharedInternals';
41 import invariant from 'shared/invariant';
@@ -94,6 +95,7 @@ import {
95 afterActiveInstanceBlur,
96 clearContainer,
97 scheduleMicrotask,
98 + getCurrentEventPriority,
99 } from './ReactFiberHostConfig';
100
101 import {
@@ -461,11 +463,23 @@ export function requestUpdateLane(fiber: Fiber): Lane {
463 const currentLanePriority = getCurrentUpdateLanePriority();
464 lane = findUpdateLane(currentLanePriority, currentEventWipLanes);
465 } else {
464 - const schedulerLanePriority = schedulerPriorityToLanePriority(
465 - schedulerPriority,
466 - );
467 -
468 - lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
466 + if (enableNativeEventPriorityInference) {
467 + const eventLanePriority = getCurrentEventPriority();
468 + if (eventLanePriority === DefaultLanePriority) {
469 + // TODO: move this case into the ReactDOM host config.
470 + const schedulerLanePriority = schedulerPriorityToLanePriority(
471 + schedulerPriority,
472 + );
473 + lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
474 + } else {
475 + lane = findUpdateLane(eventLanePriority, currentEventWipLanes);
476 + }
477 + } else {
478 + const schedulerLanePriority = schedulerPriorityToLanePriority(
479 + schedulerPriority,
480 + );
481 + lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes);
482 + }
483 }
484
485 return lane;
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
+1
@@ -73,6 +73,7 @@ export const afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur;
73 export const preparePortalMount = $$$hostConfig.preparePortalMount;
74 export const prepareScopeUpdate = $$$hostConfig.preparePortalMount;
75 export const getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
76 +export const getCurrentEventPriority = $$$hostConfig.getCurrentEventPriority;
77
78 // -------------------
79 // Test selectors
packages/react-test-renderer/src/ReactTestHostConfig.js
+12
@@ -8,6 +8,14 @@
8 */
9
10 import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols';
11 +import {enableNewReconciler} from 'shared/ReactFeatureFlags';
12 +
13 +import {DefaultLanePriority as DefaultLanePriority_old} from 'react-reconciler/src/ReactFiberLane.old';
14 +import {DefaultLanePriority as DefaultLanePriority_new} from 'react-reconciler/src/ReactFiberLane.new';
15 +
16 +const DefaultLanePriority = enableNewReconciler
17 + ? DefaultLanePriority_new
18 + : DefaultLanePriority_old;
19
20 export type Type = string;
21 export type Props = Object;
@@ -213,6 +221,10 @@ export function createTextInstance(
221 };
222 }
223
224 +export function getCurrentEventPriority(): * {
225 + return DefaultLanePriority;
226 +}
227 +
228 export const isPrimaryRenderer = false;
229 export const warnsIfNotActing = true;
230
packages/shared/ReactFeatureFlags.js
+2
@@ -151,3 +151,5 @@ export const disableSchedulerTimeoutInWorkLoop = false;
151 export const enableTransitionEntanglement = false;
152
153 export const enableDiscreteEventMicroTasks = false;
154 +
155 +export const enableNativeEventPriorityInference = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -59,6 +59,7 @@ export const enableRecursiveCommitTraversal = false;
59 export const disableSchedulerTimeoutInWorkLoop = false;
60 export const enableTransitionEntanglement = false;
61 export const enableDiscreteEventMicroTasks = false;
62 +export const enableNativeEventPriorityInference = false;
63
64 // Flow magic to verify the exports of this file match the original version.
65 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.testing.www.js
+1
@@ -58,6 +58,7 @@ export const enableRecursiveCommitTraversal = false;
58 export const disableSchedulerTimeoutInWorkLoop = false;
59 export const enableTransitionEntanglement = false;
60 export const enableDiscreteEventMicroTasks = false;
61 +export const enableNativeEventPriorityInference = false;
62
63 // Flow magic to verify the exports of this file match the original version.
64 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+1
@@ -57,3 +57,4 @@ export const enableProfilerNestedUpdateScheduledHook = __VARIANT__;
57 export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
58 export const enableTransitionEntanglement = __VARIANT__;
59 export const enableDiscreteEventMicroTasks = __VARIANT__;
60 +export const enableNativeEventPriorityInference = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -33,6 +33,7 @@ export const {
33 disableSchedulerTimeoutInWorkLoop,
34 enableTransitionEntanglement,
35 enableDiscreteEventMicroTasks,
36 + enableNativeEventPriorityInference,
37 } = dynamicFeatureFlags;
38
39 // On WWW, __EXPERIMENTAL__ is used for a new modern build.