@samitouri / QOS-React-2 / commits / 8e1462e8c4

[Fiber] Move updatePriority tracking to renderers (#28751)

Currently updatePriority is tracked in the reconciler. `flushSync` is going to be implemented reconciler agnostic soon and we need to move the tracking of this state to the renderer and out of reconciler. This change implements new renderer bin dings for getCurrentUpdatePriority and setCurrentUpdatePriority. I was originally going to have the getter also do the event priority defaulting using window.event so we eliminate getCur rentEventPriority but this makes all the callsites where we store the true current updatePriority on the stack harder to work with so for now they remain separate. I also moved runWithPriority to the renderer since it really belongs whereever the state is being managed and it is only currently exposed in the DOM renderer. Additionally the current update priority is not stored on ReactDOMSharedInternals. While not particularly meaningful in this change it opens the door to implementing `flushSync` outside of the reconciler

Josh Story committed Apr 8, 2024 at 08:53 UTC 8e1462e8c471fbec98aac2b3e1326498d0ff7139
22 files changed +217 -98
packages/react-art/src/ReactFiberConfigART.js
+18 -3
@@ -5,12 +5,17 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
9 +
10 import Transform from 'art/core/transform';
11 import Mode from 'art/modes/current';
12
13 import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
14
13 -import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
15 +import {
16 + DefaultEventPriority,
17 + NoEventPriority,
18 +} from 'react-reconciler/src/ReactEventPriorities';
19
20 const pooledTransform = new Transform();
21
@@ -336,8 +341,18 @@ export function shouldSetTextContent(type, props) {
341 );
342 }
343
339 -export function getCurrentEventPriority() {
340 - return DefaultEventPriority;
344 +let currentUpdatePriority: EventPriority = NoEventPriority;
345 +
346 +export function setCurrentUpdatePriority(newPriority: EventPriority): void {
347 + currentUpdatePriority = newPriority;
348 +}
349 +
350 +export function getCurrentUpdatePriority(): EventPriority {
351 + return currentUpdatePriority;
352 +}
353 +
354 +export function resolveUpdatePriority(): EventPriority {
355 + return currentUpdatePriority || DefaultEventPriority;
356 }
357
358 export function shouldAttemptEagerTransition() {
packages/react-dom-bindings/src/client/ReactDOMUpdatePriority.js new
+55
@@ -0,0 +1,55 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
11 +
12 +import {getEventPriority} from '../events/ReactDOMEventListener';
13 +import {
14 + NoEventPriority,
15 + DefaultEventPriority,
16 +} from 'react-reconciler/src/ReactEventPriorities';
17 +
18 +import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
19 +
20 +export function setCurrentUpdatePriority(
21 + newPriority: EventPriority,
22 + // Closure will consistently not inline this function when it has arity 1
23 + // however when it has arity 2 even if the second arg is omitted at every
24 + // callsite it seems to inline it even when the internal length of the function
25 + // is much longer. I hope this is consistent enough to rely on across builds
26 + IntentionallyUnusedArgument?: empty,
27 +): void {
28 + ReactDOMSharedInternals.up = newPriority;
29 +}
30 +
31 +export function getCurrentUpdatePriority(): EventPriority {
32 + return ReactDOMSharedInternals.up;
33 +}
34 +
35 +export function resolveUpdatePriority(): EventPriority {
36 + const updatePriority = ReactDOMSharedInternals.up;
37 + if (updatePriority !== NoEventPriority) {
38 + return updatePriority;
39 + }
40 + const currentEvent = window.event;
41 + if (currentEvent === undefined) {
42 + return DefaultEventPriority;
43 + }
44 + return getEventPriority(currentEvent.type);
45 +}
46 +
47 +export function runWithPriority<T>(priority: EventPriority, fn: () => T): T {
48 + const previousPriority = getCurrentUpdatePriority();
49 + try {
50 + setCurrentUpdatePriority(priority);
51 + return fn();
52 + } finally {
53 + setCurrentUpdatePriority(previousPriority);
54 + }
55 +}
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+5 -11
@@ -7,7 +7,6 @@
7 * @flow
8 */
9
10 -import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
10 import type {DOMEventName} from '../events/DOMEventNames';
11 import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
12 import type {
@@ -29,11 +28,15 @@ import type {
28
29 import {NotPending} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
30 import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
32 -import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
31
32 import hasOwnProperty from 'shared/hasOwnProperty';
33 import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
34
35 +export {
36 + setCurrentUpdatePriority,
37 + getCurrentUpdatePriority,
38 + resolveUpdatePriority,
39 +} from './ReactDOMUpdatePriority';
40 import {
41 precacheFiberNode,
42 updateFiberProps,
@@ -69,7 +72,6 @@ import {
72 import {
73 isEnabled as ReactBrowserEventEmitterIsEnabled,
74 setEnabled as ReactBrowserEventEmitterSetEnabled,
72 - getEventPriority,
75 } from '../events/ReactDOMEventListener';
76 import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
77 import {
@@ -572,14 +574,6 @@ export function createTextInstance(
574 return textNode;
575 }
576
575 -export function getCurrentEventPriority(): EventPriority {
576 - const currentEvent = window.event;
577 - if (currentEvent === undefined) {
578 - return DefaultEventPriority;
579 - }
580 - return getEventPriority(currentEvent.type);
581 -}
582 -
577 let currentPopstateTransitionEvent: Event | null = null;
578 export function shouldAttemptEagerTransition(): boolean {
579 const event = window.event;
packages/react-dom-bindings/src/events/ReactDOMEventListener.js
+6 -4
@@ -34,6 +34,10 @@ import {
34 } from '../client/ReactDOMComponentTree';
35
36 import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
37 +import {
38 + getCurrentUpdatePriority,
39 + setCurrentUpdatePriority,
40 +} from '../client/ReactDOMUpdatePriority';
41
42 import {
43 getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
@@ -48,8 +52,6 @@ import {
52 ContinuousEventPriority,
53 DefaultEventPriority,
54 IdleEventPriority,
51 - getCurrentUpdatePriority,
52 - setCurrentUpdatePriority,
55 } from 'react-reconciler/src/ReactEventPriorities';
56 import ReactSharedInternals from 'shared/ReactSharedInternals';
57 import {isRootDehydrated} from 'react-reconciler/src/ReactFiberShellHydration';
@@ -115,9 +117,9 @@ function dispatchDiscreteEvent(
117 container: EventTarget,
118 nativeEvent: AnyNativeEvent,
119 ) {
118 - const previousPriority = getCurrentUpdatePriority();
120 const prevTransition = ReactCurrentBatchConfig.transition;
121 ReactCurrentBatchConfig.transition = null;
122 + const previousPriority = getCurrentUpdatePriority();
123 try {
124 setCurrentUpdatePriority(DiscreteEventPriority);
125 dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
@@ -133,9 +135,9 @@ function dispatchContinuousEvent(
135 container: EventTarget,
136 nativeEvent: AnyNativeEvent,
137 ) {
136 - const previousPriority = getCurrentUpdatePriority();
138 const prevTransition = ReactCurrentBatchConfig.transition;
139 ReactCurrentBatchConfig.transition = null;
140 + const previousPriority = getCurrentUpdatePriority();
141 try {
142 setCurrentUpdatePriority(ContinuousEventPriority);
143 dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js
+4 -4
@@ -37,15 +37,15 @@ import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
37 import {isHigherEventPriority} from 'react-reconciler/src/ReactEventPriorities';
38 import {isRootDehydrated} from 'react-reconciler/src/ReactFiberShellHydration';
39 import {dispatchReplayedFormAction} from './plugins/FormActionEventPlugin';
40 +import {
41 + getCurrentUpdatePriority,
42 + runWithPriority as attemptHydrationAtPriority,
43 +} from '../client/ReactDOMUpdatePriority';
44
45 import {
46 attemptContinuousHydration,
47 attemptHydrationAtCurrentPriority,
48 } from 'react-reconciler/src/ReactFiberReconciler';
45 -import {
46 - runWithPriority as attemptHydrationAtPriority,
47 - getCurrentUpdatePriority,
48 -} from 'react-reconciler/src/ReactEventPriorities';
49
50 // TODO: Upgrade this definition once we're on a newer version of Flow that
51 // has this definition built-in.
packages/react-dom/src/ReactDOMSharedInternals.js
+8 -3
@@ -7,8 +7,11 @@
7 * @flow
8 */
9
10 +import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
11 import type {HostDispatcher} from './shared/ReactDOMTypes';
12
13 +import {NoEventPriority} from 'react-reconciler/src/ReactEventPriorities';
14 +
15 type InternalsType = {
16 usingClientEntryPoint: boolean,
17 Events: [any, any, any, any, any, any],
@@ -20,6 +23,7 @@ type InternalsType = {
23 | ((
24 componentOrElement: React$Component<any, any>,
25 ) => null | Element | Text),
26 + up /* currentUpdatePriority */: EventPriority,
27 };
28
29 function noop() {}
@@ -34,13 +38,14 @@ const DefaultDispatcher: HostDispatcher = {
38 preinitModuleScript: noop,
39 };
40
37 -const Internals: InternalsType = ({
41 +const Internals: InternalsType = {
42 usingClientEntryPoint: false,
39 - Events: null,
43 + Events: (null: any),
44 ReactDOMCurrentDispatcher: {
45 current: DefaultDispatcher,
46 },
47 findDOMNode: null,
44 -}: any);
48 + up /* currentUpdatePriority */: NoEventPriority,
49 +};
50
51 export default Internals;
packages/react-dom/src/client/ReactDOM.js
+1 -1
@@ -20,6 +20,7 @@ import {
20 isValidContainer,
21 } from './ReactDOMRoot';
22 import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';
23 +import {runWithPriority} from 'react-dom-bindings/src/client/ReactDOMUpdatePriority';
24
25 import {
26 flushSync as flushSyncWithoutWarningIfAlreadyRendering,
@@ -27,7 +28,6 @@ import {
28 injectIntoDevTools,
29 findHostInstance,
30 } from 'react-reconciler/src/ReactFiberReconciler';
30 -import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
31 import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
32 import {canUseDOM} from 'shared/ExecutionEnvironment';
33 import ReactVersion from 'shared/ReactVersion';
packages/react-native-renderer/src/ReactFiberConfigFabric.js
+15 -1
@@ -15,6 +15,7 @@ import type {
15 import {create, diff} from './ReactNativeAttributePayload';
16 import {dispatchEvent} from './ReactFabricEventEmitter';
17 import {
18 + NoEventPriority,
19 DefaultEventPriority,
20 DiscreteEventPriority,
21 type EventPriority,
@@ -311,7 +312,20 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
312 return false;
313 }
314
314 -export function getCurrentEventPriority(): EventPriority {
315 +let currentUpdatePriority: EventPriority = NoEventPriority;
316 +export function setCurrentUpdatePriority(newPriority: EventPriority): void {
317 + currentUpdatePriority = newPriority;
318 +}
319 +
320 +export function getCurrentUpdatePriority(): EventPriority {
321 + return currentUpdatePriority;
322 +}
323 +
324 +export function resolveUpdatePriority(): EventPriority {
325 + if (currentUpdatePriority !== NoEventPriority) {
326 + return currentUpdatePriority;
327 + }
328 +
329 const currentEventPriority = fabricGetCurrentEventPriority
330 ? fabricGetCurrentEventPriority()
331 : null;
packages/react-native-renderer/src/ReactFiberConfigNative.js
+14 -1
@@ -26,6 +26,7 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
26
27 import {
28 DefaultEventPriority,
29 + NoEventPriority,
30 type EventPriority,
31 } from 'react-reconciler/src/ReactEventPriorities';
32 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
@@ -253,7 +254,19 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
254 return false;
255 }
256
256 -export function getCurrentEventPriority(): EventPriority {
257 +let currentUpdatePriority: EventPriority = NoEventPriority;
258 +export function setCurrentUpdatePriority(newPriority: EventPriority): void {
259 + currentUpdatePriority = newPriority;
260 +}
261 +
262 +export function getCurrentUpdatePriority(): EventPriority {
263 + return currentUpdatePriority;
264 +}
265 +
266 +export function resolveUpdatePriority(): EventPriority {
267 + if (currentUpdatePriority !== NoEventPriority) {
268 + return currentUpdatePriority;
269 + }
270 return DefaultEventPriority;
271 }
272
packages/react-noop-renderer/src/createReactNoop.js
+30 -2
@@ -21,12 +21,14 @@ import type {
21 import type {UpdateQueue} from 'react-reconciler/src/ReactFiberClassUpdateQueue';
22 import type {ReactNodeList} from 'shared/ReactTypes';
23 import type {RootTag} from 'react-reconciler/src/ReactRootTags';
24 +import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
25
26 import * as Scheduler from 'scheduler/unstable_mock';
27 import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
28 import isArray from 'shared/isArray';
29 import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
30 import {
31 + NoEventPriority,
32 DefaultEventPriority,
33 IdleEventPriority,
34 ConcurrentRoot,
@@ -512,7 +514,13 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
514
515 resetAfterCommit(): void {},
516
515 - getCurrentEventPriority() {
517 + setCurrentUpdatePriority,
518 + getCurrentUpdatePriority,
519 +
520 + resolveUpdatePriority() {
521 + if (currentUpdatePriority !== NoEventPriority) {
522 + return currentUpdatePriority;
523 + }
524 return currentEventPriority;
525 },
526
@@ -786,6 +794,15 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
794 const roots = new Map();
795 const DEFAULT_ROOT_ID = '<default>';
796
797 + let currentUpdatePriority = NoEventPriority;
798 + function setCurrentUpdatePriority(newPriority: EventPriority): void {
799 + currentUpdatePriority = newPriority;
800 + }
801 +
802 + function getCurrentUpdatePriority(): EventPriority {
803 + return currentUpdatePriority;
804 + }
805 +
806 let currentEventPriority = DefaultEventPriority;
807
808 function createJSXElementForTestComparison(type, props) {
@@ -1223,7 +1240,18 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
1240 return Scheduler.unstable_flushExpired();
1241 },
1242
1226 - unstable_runWithPriority: NoopRenderer.runWithPriority,
1243 + unstable_runWithPriority: function runWithPriority<T>(
1244 + priority: EventPriority,
1245 + fn: () => T,
1246 + ): T {
1247 + const previousPriority = getCurrentUpdatePriority();
1248 + try {
1249 + setCurrentUpdatePriority(priority);
1250 + return fn();
1251 + } finally {
1252 + setCurrentUpdatePriority(previousPriority);
1253 + }
1254 + },
1255
1256 batchedUpdates: NoopRenderer.batchedUpdates,
1257
packages/react-reconciler/src/ReactEventPriorities.js
+5 -20
@@ -21,31 +21,12 @@ import {
21
22 export opaque type EventPriority = Lane;
23
24 +export const NoEventPriority: EventPriority = NoLane;
25 export const DiscreteEventPriority: EventPriority = SyncLane;
26 export const ContinuousEventPriority: EventPriority = InputContinuousLane;
27 export const DefaultEventPriority: EventPriority = DefaultLane;
28 export const IdleEventPriority: EventPriority = IdleLane;
29
29 -let currentUpdatePriority: EventPriority = NoLane;
30 -
31 -export function getCurrentUpdatePriority(): EventPriority {
32 - return currentUpdatePriority;
33 -}
34 -
35 -export function setCurrentUpdatePriority(newPriority: EventPriority) {
36 - currentUpdatePriority = newPriority;
37 -}
38 -
39 -export function runWithPriority<T>(priority: EventPriority, fn: () => T): T {
40 - const previousPriority = currentUpdatePriority;
41 - try {
42 - currentUpdatePriority = priority;
43 - return fn();
44 - } finally {
45 - currentUpdatePriority = previousPriority;
46 - }
47 -}
48 -
30 export function higherEventPriority(
31 a: EventPriority,
32 b: EventPriority,
@@ -67,6 +48,10 @@ export function isHigherEventPriority(
48 return a !== 0 && a < b;
49 }
50
51 +export function eventPriorityToLane(updatePriority: EventPriority): Lane {
52 + return updatePriority;
53 +}
54 +
55 export function lanesToEventPriority(lanes: Lanes): EventPriority {
56 const lane = getHighestPriorityLane(lanes);
57 if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
packages/react-reconciler/src/ReactFiberBeginWork.js
+1 -1
@@ -678,7 +678,7 @@ function updateOffscreenComponent(
678 // pending work. We can't read `childLanes` from the current Offscreen
679 // fiber because we reset it when it was deferred; however, we can read
680 // the pending lanes from the child fibers.
681 - let currentChildLanes = NoLanes;
681 + let currentChildLanes: Lanes = NoLanes;
682 while (currentChild !== null) {
683 currentChildLanes = mergeLanes(
684 mergeLanes(currentChildLanes, currentChild.lanes),
packages/react-reconciler/src/ReactFiberClassUpdateQueue.js
+1 -1
@@ -556,7 +556,7 @@ export function processUpdateQueue<State>(
556 let newState = queue.baseState;
557 // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
558 // from the original lanes.
559 - let newLanes = NoLanes;
559 + let newLanes: Lanes = NoLanes;
560
561 let newBaseState = null;
562 let newFirstBaseUpdate = null;
packages/react-reconciler/src/ReactFiberCompleteWork.js
+1 -1
@@ -742,7 +742,7 @@ function bubbleProperties(completedWork: Fiber) {
742 completedWork.alternate !== null &&
743 completedWork.alternate.child === completedWork.child;
744
745 - let newChildLanes = NoLanes;
745 + let newChildLanes: Lanes = NoLanes;
746 let subtreeFlags = NoFlags;
747
748 if (!didBailout) {
packages/react-reconciler/src/ReactFiberHooks.js
+5 -3
@@ -27,7 +27,11 @@ import type {HookFlags} from './ReactHookEffectTags';
27 import type {Flags} from './ReactFiberFlags';
28 import type {TransitionStatus} from './ReactFiberConfig';
29
30 -import {NotPendingTransition as NoPendingHostTransition} from './ReactFiberConfig';
30 +import {
31 + NotPendingTransition as NoPendingHostTransition,
32 + setCurrentUpdatePriority,
33 + getCurrentUpdatePriority,
34 +} from './ReactFiberConfig';
35 import ReactSharedInternals from 'shared/ReactSharedInternals';
36 import {
37 enableDebugTracing,
@@ -74,8 +78,6 @@ import {
78 } from './ReactFiberLane';
79 import {
80 ContinuousEventPriority,
77 - getCurrentUpdatePriority,
78 - setCurrentUpdatePriority,
81 higherEventPriority,
82 } from './ReactEventPriorities';
83 import {readContext, checkIfContextChanged} from './ReactFiberNewContext';
packages/react-reconciler/src/ReactFiberLane.js
+1 -1
@@ -223,7 +223,7 @@ export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
223 return NoLanes;
224 }
225
226 - let nextLanes = NoLanes;
226 + let nextLanes: Lanes = NoLanes;
227
228 const suspendedLanes = root.suspendedLanes;
229 const pingedLanes = root.pingedLanes;
packages/react-reconciler/src/ReactFiberReconciler.js
-6
@@ -86,10 +86,6 @@ import {
86 getHighestPriorityPendingLanes,
87 higherPriorityLane,
88 } from './ReactFiberLane';
89 -import {
90 - getCurrentUpdatePriority,
91 - runWithPriority,
92 -} from './ReactEventPriorities';
89 import {
90 scheduleRefresh,
91 scheduleRoot,
@@ -525,8 +521,6 @@ export function attemptHydrationAtCurrentPriority(fiber: Fiber): void {
521 markRetryLaneIfNotHydrated(fiber, lane);
522 }
523
528 -export {getCurrentUpdatePriority, runWithPriority};
529 -
524 export {findHostInstance};
525
526 export {findHostInstanceWithWarning};
packages/react-reconciler/src/ReactFiberWorkLoop.js
+15 -32
@@ -71,11 +71,13 @@ import {
71 cancelTimeout,
72 noTimeout,
73 afterActiveInstanceBlur,
74 - getCurrentEventPriority,
74 startSuspendingCommit,
75 waitForCommitToBeReady,
76 preloadInstance,
77 supportsHydration,
78 + setCurrentUpdatePriority,
79 + getCurrentUpdatePriority,
80 + resolveUpdatePriority,
81 } from './ReactFiberConfig';
82
83 import {createWorkInProgress, resetWorkInProgress} from './ReactFiber';
@@ -158,10 +160,9 @@ import {
160 import {
161 DiscreteEventPriority,
162 DefaultEventPriority,
161 - getCurrentUpdatePriority,
162 - setCurrentUpdatePriority,
163 lowerEventPriority,
164 lanesToEventPriority,
165 + eventPriorityToLane,
166 } from './ReactEventPriorities';
167 import {requestCurrentTransition} from './ReactFiberTransition';
168 import {
@@ -642,25 +643,7 @@ export function requestUpdateLane(fiber: Fiber): Lane {
643 requestTransitionLane(transition);
644 }
645
645 - // Updates originating inside certain React methods, like flushSync, have
646 - // their priority set by tracking it with a context variable.
647 - //
648 - // The opaque type returned by the host config is internally a lane, so we can
649 - // use that directly.
650 - // TODO: Move this type conversion to the event priority module.
651 - const updateLane: Lane = (getCurrentUpdatePriority(): any);
652 - if (updateLane !== NoLane) {
653 - return updateLane;
654 - }
655 -
656 - // This update originated outside React. Ask the host environment for an
657 - // appropriate priority, based on the type of event.
658 - //
659 - // The opaque type returned by the host config is internally a lane, so we can
660 - // use that directly.
661 - // TODO: Move this type conversion to the event priority module.
662 - const eventLane: Lane = (getCurrentEventPriority(): any);
663 - return eventLane;
646 + return eventPriorityToLane(resolveUpdatePriority());
647 }
648
649 function requestRetryLane(fiber: Fiber) {
@@ -1447,12 +1430,12 @@ export function getExecutionContext(): ExecutionContext {
1430 }
1431
1432 export function deferredUpdates<A>(fn: () => A): A {
1450 - const previousPriority = getCurrentUpdatePriority();
1433 const prevTransition = ReactCurrentBatchConfig.transition;
1434
1435 + const previousPriority = getCurrentUpdatePriority();
1436 try {
1454 - ReactCurrentBatchConfig.transition = null;
1437 setCurrentUpdatePriority(DefaultEventPriority);
1438 + ReactCurrentBatchConfig.transition = null;
1439 return fn();
1440 } finally {
1441 setCurrentUpdatePriority(previousPriority);
@@ -1493,11 +1476,11 @@ export function discreteUpdates<A, B, C, D, R>(
1476 c: C,
1477 d: D,
1478 ): R {
1496 - const previousPriority = getCurrentUpdatePriority();
1479 const prevTransition = ReactCurrentBatchConfig.transition;
1480 + const previousPriority = getCurrentUpdatePriority();
1481 try {
1499 - ReactCurrentBatchConfig.transition = null;
1482 setCurrentUpdatePriority(DiscreteEventPriority);
1483 + ReactCurrentBatchConfig.transition = null;
1484 return fn(a, b, c, d);
1485 } finally {
1486 setCurrentUpdatePriority(previousPriority);
@@ -1534,8 +1517,8 @@ export function flushSync<R>(fn: (() => R) | void): R | void {
1517 const previousPriority = getCurrentUpdatePriority();
1518
1519 try {
1537 - ReactCurrentBatchConfig.transition = null;
1520 setCurrentUpdatePriority(DiscreteEventPriority);
1521 + ReactCurrentBatchConfig.transition = null;
1522 if (fn) {
1523 return fn();
1524 } else {
@@ -2716,12 +2699,12 @@ function commitRoot(
2699 ) {
2700 // TODO: This no longer makes any sense. We already wrap the mutation and
2701 // layout phases. Should be able to remove.
2719 - const previousUpdateLanePriority = getCurrentUpdatePriority();
2702 const prevTransition = ReactCurrentBatchConfig.transition;
2703
2704 + const previousUpdateLanePriority = getCurrentUpdatePriority();
2705 try {
2723 - ReactCurrentBatchConfig.transition = null;
2706 setCurrentUpdatePriority(DiscreteEventPriority);
2707 + ReactCurrentBatchConfig.transition = null;
2708 commitRootImpl(
2709 root,
2710 recoverableErrors,
@@ -3190,8 +3173,8 @@ export function flushPassiveEffects(): boolean {
3173 const previousPriority = getCurrentUpdatePriority();
3174
3175 try {
3193 - ReactCurrentBatchConfig.transition = null;
3176 setCurrentUpdatePriority(priority);
3177 + ReactCurrentBatchConfig.transition = null;
3178 return flushPassiveEffectsImpl();
3179 } finally {
3180 setCurrentUpdatePriority(previousPriority);
@@ -3546,7 +3529,7 @@ function retryTimedOutBoundary(boundaryFiber: Fiber, retryLane: Lane) {
3529
3530 export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {
3531 const suspenseState: null | SuspenseState = boundaryFiber.memoizedState;
3549 - let retryLane = NoLane;
3532 + let retryLane: Lane = NoLane;
3533 if (suspenseState !== null) {
3534 retryLane = suspenseState.retryLane;
3535 }
@@ -3554,7 +3537,7 @@ export function retryDehydratedSuspenseBoundary(boundaryFiber: Fiber) {
3537 }
3538
3539 export function resolveRetryWakeable(boundaryFiber: Fiber, wakeable: Wakeable) {
3557 - let retryLane = NoLane; // Default
3540 + let retryLane: Lane = NoLane; // Default
3541 let retryCache: WeakSet<Wakeable> | Set<Wakeable> | null;
3542 switch (boundaryFiber.tag) {
3543 case SuspenseComponent:
packages/react-reconciler/src/ReactReconcilerConstants.js
+1
@@ -11,6 +11,7 @@
11 // Only expose the minimal subset necessary to implement a host config.
12
13 export {
14 + NoEventPriority,
15 DiscreteEventPriority,
16 ContinuousEventPriority,
17 DefaultEventPriority,
packages/react-reconciler/src/__tests__/ReactFiberHostContext-test.internal.js
+14 -1
@@ -15,6 +15,7 @@ let act;
15 let ReactFiberReconciler;
16 let ConcurrentRoot;
17 let DefaultEventPriority;
18 +let NoEventPriority;
19
20 describe('ReactFiberHostContext', () => {
21 beforeEach(() => {
@@ -26,6 +27,8 @@ describe('ReactFiberHostContext', () => {
27 require('react-reconciler/src/ReactRootTags').ConcurrentRoot;
28 DefaultEventPriority =
29 require('react-reconciler/src/ReactEventPriorities').DefaultEventPriority;
30 + NoEventPriority =
31 + require('react-reconciler/src/ReactEventPriorities').NoEventPriority;
32 });
33
34 global.IS_REACT_ACT_ENVIRONMENT = true;
@@ -34,6 +37,7 @@ describe('ReactFiberHostContext', () => {
37 it('should send the context to prepareForCommit and resetAfterCommit', () => {
38 const rootContext = {};
39 const childContext = {};
40 + let updatePriority: typeof DefaultEventPriority = NoEventPriority;
41 const Renderer = ReactFiberReconciler({
42 prepareForCommit: function (hostContext) {
43 expect(hostContext).toBe(rootContext);
@@ -67,7 +71,16 @@ describe('ReactFiberHostContext', () => {
71 return null;
72 },
73 clearContainer: function () {},
70 - getCurrentEventPriority: function () {
74 + setCurrentUpdatePriority: function (newPriority: any) {
75 + updatePriority = newPriority;
76 + },
77 + getCurrentUpdatePriority: function () {
78 + return updatePriority;
79 + },
80 + resolveUpdatePriority: function () {
81 + if (updatePriority !== NoEventPriority) {
82 + return updatePriority;
83 + }
84 return DefaultEventPriority;
85 },
86 shouldAttemptEagerTransition() {
packages/react-reconciler/src/forks/ReactFiberConfig.custom.js
+3 -1
@@ -65,7 +65,9 @@ export const afterActiveInstanceBlur = $$$config.afterActiveInstanceBlur;
65 export const preparePortalMount = $$$config.preparePortalMount;
66 export const prepareScopeUpdate = $$$config.prepareScopeUpdate;
67 export const getInstanceFromScope = $$$config.getInstanceFromScope;
68 -export const getCurrentEventPriority = $$$config.getCurrentEventPriority;
68 +export const setCurrentUpdatePriority = $$$config.setCurrentUpdatePriority;
69 +export const getCurrentUpdatePriority = $$$config.getCurrentUpdatePriority;
70 +export const resolveUpdatePriority = $$$config.resolveUpdatePriority;
71 export const shouldAttemptEagerTransition =
72 $$$config.shouldAttemptEagerTransition;
73 export const detachDeletedInstance = $$$config.detachDeletedInstance;
packages/react-test-renderer/src/ReactFiberConfigTestHost.js
+14 -1
@@ -10,6 +10,7 @@
10 import isArray from 'shared/isArray';
11 import {
12 DefaultEventPriority,
13 + NoEventPriority,
14 type EventPriority,
15 } from 'react-reconciler/src/ReactEventPriorities';
16
@@ -201,7 +202,19 @@ export function createTextInstance(
202 };
203 }
204
204 -export function getCurrentEventPriority(): EventPriority {
205 +let currentUpdatePriority: EventPriority = NoEventPriority;
206 +export function setCurrentUpdatePriority(newPriority: EventPriority): void {
207 + currentUpdatePriority = newPriority;
208 +}
209 +
210 +export function getCurrentUpdatePriority(): EventPriority {
211 + return currentUpdatePriority;
212 +}
213 +
214 +export function resolveUpdatePriority(): EventPriority {
215 + if (currentUpdatePriority !== NoEventPriority) {
216 + return currentUpdatePriority;
217 + }
218 return DefaultEventPriority;
219 }
220 export function shouldAttemptEagerTransition(): boolean {