Skip to content

Commit 8e1462e

Browse files
authored
[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
1 parent 0b3b8a6 commit 8e1462e

22 files changed

+217
-98
lines changed

packages/react-art/src/ReactFiberConfigART.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
9+
810
import Transform from 'art/core/transform';
911
import Mode from 'art/modes/current';
1012

1113
import {TYPES, EVENT_TYPES, childrenAsString} from './ReactARTInternals';
1214

13-
import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
15+
import {
16+
DefaultEventPriority,
17+
NoEventPriority,
18+
} from 'react-reconciler/src/ReactEventPriorities';
1419

1520
const pooledTransform = new Transform();
1621

@@ -336,8 +341,18 @@ export function shouldSetTextContent(type, props) {
336341
);
337342
}
338343

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;
341356
}
342357

343358
export function shouldAttemptEagerTransition() {
Original file line numberDiff line numberDiff line change
@@ -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
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* @flow
88
*/
99

10-
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
1110
import type {DOMEventName} from '../events/DOMEventNames';
1211
import type {Fiber, FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
1312
import type {
@@ -29,11 +28,15 @@ import type {
2928

3029
import {NotPending} from 'react-dom-bindings/src/shared/ReactDOMFormActions';
3130
import {getCurrentRootHostContainer} from 'react-reconciler/src/ReactFiberHostContext';
32-
import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
3331

3432
import hasOwnProperty from 'shared/hasOwnProperty';
3533
import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
3634

35+
export {
36+
setCurrentUpdatePriority,
37+
getCurrentUpdatePriority,
38+
resolveUpdatePriority,
39+
} from './ReactDOMUpdatePriority';
3740
import {
3841
precacheFiberNode,
3942
updateFiberProps,
@@ -69,7 +72,6 @@ import {
6972
import {
7073
isEnabled as ReactBrowserEventEmitterIsEnabled,
7174
setEnabled as ReactBrowserEventEmitterSetEnabled,
72-
getEventPriority,
7375
} from '../events/ReactDOMEventListener';
7476
import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
7577
import {
@@ -572,14 +574,6 @@ export function createTextInstance(
572574
return textNode;
573575
}
574576

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-
583577
let currentPopstateTransitionEvent: Event | null = null;
584578
export function shouldAttemptEagerTransition(): boolean {
585579
const event = window.event;

packages/react-dom-bindings/src/events/ReactDOMEventListener.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ import {
3434
} from '../client/ReactDOMComponentTree';
3535

3636
import {dispatchEventForPluginEventSystem} from './DOMPluginEventSystem';
37+
import {
38+
getCurrentUpdatePriority,
39+
setCurrentUpdatePriority,
40+
} from '../client/ReactDOMUpdatePriority';
3741

3842
import {
3943
getCurrentPriorityLevel as getCurrentSchedulerPriorityLevel,
@@ -48,8 +52,6 @@ import {
4852
ContinuousEventPriority,
4953
DefaultEventPriority,
5054
IdleEventPriority,
51-
getCurrentUpdatePriority,
52-
setCurrentUpdatePriority,
5355
} from 'react-reconciler/src/ReactEventPriorities';
5456
import ReactSharedInternals from 'shared/ReactSharedInternals';
5557
import {isRootDehydrated} from 'react-reconciler/src/ReactFiberShellHydration';
@@ -115,9 +117,9 @@ function dispatchDiscreteEvent(
115117
container: EventTarget,
116118
nativeEvent: AnyNativeEvent,
117119
) {
118-
const previousPriority = getCurrentUpdatePriority();
119120
const prevTransition = ReactCurrentBatchConfig.transition;
120121
ReactCurrentBatchConfig.transition = null;
122+
const previousPriority = getCurrentUpdatePriority();
121123
try {
122124
setCurrentUpdatePriority(DiscreteEventPriority);
123125
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
@@ -133,9 +135,9 @@ function dispatchContinuousEvent(
133135
container: EventTarget,
134136
nativeEvent: AnyNativeEvent,
135137
) {
136-
const previousPriority = getCurrentUpdatePriority();
137138
const prevTransition = ReactCurrentBatchConfig.transition;
138139
ReactCurrentBatchConfig.transition = null;
140+
const previousPriority = getCurrentUpdatePriority();
139141
try {
140142
setCurrentUpdatePriority(ContinuousEventPriority);
141143
dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);

packages/react-dom-bindings/src/events/ReactDOMEventReplaying.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ import {HostRoot, SuspenseComponent} from 'react-reconciler/src/ReactWorkTags';
3737
import {isHigherEventPriority} from 'react-reconciler/src/ReactEventPriorities';
3838
import {isRootDehydrated} from 'react-reconciler/src/ReactFiberShellHydration';
3939
import {dispatchReplayedFormAction} from './plugins/FormActionEventPlugin';
40+
import {
41+
getCurrentUpdatePriority,
42+
runWithPriority as attemptHydrationAtPriority,
43+
} from '../client/ReactDOMUpdatePriority';
4044

4145
import {
4246
attemptContinuousHydration,
4347
attemptHydrationAtCurrentPriority,
4448
} from 'react-reconciler/src/ReactFiberReconciler';
45-
import {
46-
runWithPriority as attemptHydrationAtPriority,
47-
getCurrentUpdatePriority,
48-
} from 'react-reconciler/src/ReactEventPriorities';
4949

5050
// TODO: Upgrade this definition once we're on a newer version of Flow that
5151
// has this definition built-in.

packages/react-dom/src/ReactDOMSharedInternals.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
* @flow
88
*/
99

10+
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
1011
import type {HostDispatcher} from './shared/ReactDOMTypes';
1112

13+
import {NoEventPriority} from 'react-reconciler/src/ReactEventPriorities';
14+
1215
type InternalsType = {
1316
usingClientEntryPoint: boolean,
1417
Events: [any, any, any, any, any, any],
@@ -20,6 +23,7 @@ type InternalsType = {
2023
| ((
2124
componentOrElement: React$Component<any, any>,
2225
) => null | Element | Text),
26+
up /* currentUpdatePriority */: EventPriority,
2327
};
2428

2529
function noop() {}
@@ -34,13 +38,14 @@ const DefaultDispatcher: HostDispatcher = {
3438
preinitModuleScript: noop,
3539
};
3640

37-
const Internals: InternalsType = ({
41+
const Internals: InternalsType = {
3842
usingClientEntryPoint: false,
39-
Events: null,
43+
Events: (null: any),
4044
ReactDOMCurrentDispatcher: {
4145
current: DefaultDispatcher,
4246
},
4347
findDOMNode: null,
44-
}: any);
48+
up /* currentUpdatePriority */: NoEventPriority,
49+
};
4550

4651
export default Internals;

packages/react-dom/src/client/ReactDOM.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ import {
2020
isValidContainer,
2121
} from './ReactDOMRoot';
2222
import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle';
23+
import {runWithPriority} from 'react-dom-bindings/src/client/ReactDOMUpdatePriority';
2324

2425
import {
2526
flushSync as flushSyncWithoutWarningIfAlreadyRendering,
2627
isAlreadyRendering,
2728
injectIntoDevTools,
2829
findHostInstance,
2930
} from 'react-reconciler/src/ReactFiberReconciler';
30-
import {runWithPriority} from 'react-reconciler/src/ReactEventPriorities';
3131
import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
3232
import {canUseDOM} from 'shared/ExecutionEnvironment';
3333
import ReactVersion from 'shared/ReactVersion';

packages/react-native-renderer/src/ReactFiberConfigFabric.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
import {create, diff} from './ReactNativeAttributePayload';
1616
import {dispatchEvent} from './ReactFabricEventEmitter';
1717
import {
18+
NoEventPriority,
1819
DefaultEventPriority,
1920
DiscreteEventPriority,
2021
type EventPriority,
@@ -311,7 +312,20 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
311312
return false;
312313
}
313314

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+
315329
const currentEventPriority = fabricGetCurrentEventPriority
316330
? fabricGetCurrentEventPriority()
317331
: null;

packages/react-native-renderer/src/ReactFiberConfigNative.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
2626

2727
import {
2828
DefaultEventPriority,
29+
NoEventPriority,
2930
type EventPriority,
3031
} from 'react-reconciler/src/ReactEventPriorities';
3132
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
@@ -253,7 +254,19 @@ export function shouldSetTextContent(type: string, props: Props): boolean {
253254
return false;
254255
}
255256

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+
}
257270
return DefaultEventPriority;
258271
}
259272

packages/react-noop-renderer/src/createReactNoop.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ import type {
2121
import type {UpdateQueue} from 'react-reconciler/src/ReactFiberClassUpdateQueue';
2222
import type {ReactNodeList} from 'shared/ReactTypes';
2323
import type {RootTag} from 'react-reconciler/src/ReactRootTags';
24+
import type {EventPriority} from 'react-reconciler/src/ReactEventPriorities';
2425

2526
import * as Scheduler from 'scheduler/unstable_mock';
2627
import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';
2728
import isArray from 'shared/isArray';
2829
import {checkPropStringCoercion} from 'shared/CheckStringCoercion';
2930
import {
31+
NoEventPriority,
3032
DefaultEventPriority,
3133
IdleEventPriority,
3234
ConcurrentRoot,
@@ -512,7 +514,13 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
512514

513515
resetAfterCommit(): void {},
514516

515-
getCurrentEventPriority() {
517+
setCurrentUpdatePriority,
518+
getCurrentUpdatePriority,
519+
520+
resolveUpdatePriority() {
521+
if (currentUpdatePriority !== NoEventPriority) {
522+
return currentUpdatePriority;
523+
}
516524
return currentEventPriority;
517525
},
518526

@@ -786,6 +794,15 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
786794
const roots = new Map();
787795
const DEFAULT_ROOT_ID = '<default>';
788796

797+
let currentUpdatePriority = NoEventPriority;
798+
function setCurrentUpdatePriority(newPriority: EventPriority): void {
799+
currentUpdatePriority = newPriority;
800+
}
801+
802+
function getCurrentUpdatePriority(): EventPriority {
803+
return currentUpdatePriority;
804+
}
805+
789806
let currentEventPriority = DefaultEventPriority;
790807

791808
function createJSXElementForTestComparison(type, props) {
@@ -1223,7 +1240,18 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
12231240
return Scheduler.unstable_flushExpired();
12241241
},
12251242

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+
},
12271255

12281256
batchedUpdates: NoopRenderer.batchedUpdates,
12291257

0 commit comments

Comments
 (0)