@samitouri / QOS-React-2 / commits / d3b8ff6e58

Unify BatchConfigTransition and Transition types (#32783)

This is some overdue refactoring. The two types never made sense. It also should be defined by isomorphic since it defines how it should be used by renderers rather than isomorphic depending on Fiber. Clean up hidden classes to be consistent. Fix missing name due to wrong types. I choose not to invoke the transition tracing callbacks if there's no name since the name is required there.

Sebastian Markbåge committed Mar 31, 2025 at 19:59 UTC d3b8ff6e589bcacfd1c9b0aa48c42fd1c93001c1
12 files changed +100 -99
packages/react-reconciler/src/ReactFiberActivityComponent.js
+2 -4
@@ -10,10 +10,8 @@
10 import type {ReactNodeList, OffscreenMode, Wakeable} from 'shared/ReactTypes';
11 import type {Lanes} from './ReactFiberLane';
12 import type {SpawnedCachePool} from './ReactFiberCacheComponent';
13 -import type {
14 - Transition,
15 - TracingMarkerInstance,
16 -} from './ReactFiberTracingMarkerComponent';
13 +import type {Transition} from 'react/src/ReactStartTransition';
14 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
15 import type {RetryQueue} from './ReactFiberSuspenseComponent';
16
17 export type OffscreenProps = {
packages/react-reconciler/src/ReactFiberAsyncAction.js
+2 -2
@@ -13,7 +13,7 @@ import type {
13 RejectedThenable,
14 } from 'shared/ReactTypes';
15 import type {Lane} from './ReactFiberLane';
16 -import type {BatchConfigTransition} from './ReactFiberTracingMarkerComponent';
16 +import type {Transition} from 'react/src/ReactStartTransition';
17
18 import {requestTransitionLane} from './ReactFiberRootScheduler';
19 import {NoLane} from './ReactFiberLane';
@@ -46,7 +46,7 @@ let currentEntangledLane: Lane = NoLane;
46 let currentEntangledActionThenable: Thenable<void> | null = null;
47
48 export function entangleAsyncAction<S>(
49 - transition: BatchConfigTransition,
49 + transition: Transition,
50 thenable: Thenable<S>,
51 ): Thenable<S> {
52 // `thenable` is the return value of the async action scope function. Create
packages/react-reconciler/src/ReactFiberCommitWork.js
+1 -1
@@ -30,8 +30,8 @@ import type {
30 } from './ReactFiberActivityComponent';
31 import type {Cache} from './ReactFiberCacheComponent';
32 import type {RootState} from './ReactFiberRoot';
33 +import type {Transition} from 'react/src/ReactStartTransition';
34 import type {
34 - Transition,
35 TracingMarkerInstance,
36 TransitionAbort,
37 } from './ReactFiberTracingMarkerComponent';
packages/react-reconciler/src/ReactFiberHooks.js
+17 -16
@@ -149,7 +149,7 @@ import {
149 SuspenseActionException,
150 } from './ReactFiberThenable';
151 import type {ThenableState} from './ReactFiberThenable';
152 -import type {BatchConfigTransition} from './ReactFiberTracingMarkerComponent';
152 +import type {Transition} from 'react/src/ReactStartTransition';
153 import {
154 peekEntangledActionLane,
155 peekEntangledActionThenable,
@@ -2137,11 +2137,15 @@ function runActionStateAction<S, P>(
2137
2138 // This is a fork of startTransition
2139 const prevTransition = ReactSharedInternals.T;
2140 - const currentTransition: BatchConfigTransition = {};
2141 - ReactSharedInternals.T = currentTransition;
2140 + const currentTransition: Transition = ({}: any);
2141 + if (enableTransitionTracing) {
2142 + currentTransition.name = null;
2143 + currentTransition.startTime = -1;
2144 + }
2145 if (__DEV__) {
2143 - ReactSharedInternals.T._updatedFibers = new Set();
2146 + currentTransition._updatedFibers = new Set();
2147 }
2148 + ReactSharedInternals.T = currentTransition;
2149 try {
2150 const returnValue = action(prevState, payload);
2151 const onStartTransitionFinish = ReactSharedInternals.S;
@@ -3012,7 +3016,15 @@ function startTransition<S>(
3016 );
3017
3018 const prevTransition = ReactSharedInternals.T;
3015 - const currentTransition: BatchConfigTransition = {};
3019 + const currentTransition: Transition = ({}: any);
3020 + if (enableTransitionTracing) {
3021 + currentTransition.name =
3022 + options !== undefined && options.name !== undefined ? options.name : null;
3023 + currentTransition.startTime = now();
3024 + }
3025 + if (__DEV__) {
3026 + currentTransition._updatedFibers = new Set();
3027 + }
3028
3029 // We don't really need to use an optimistic update here, because we
3030 // schedule a second "revert" update below (which we use to suspend the
@@ -3023,17 +3035,6 @@ function startTransition<S>(
3035 ReactSharedInternals.T = currentTransition;
3036 dispatchOptimisticSetState(fiber, false, queue, pendingState);
3037
3026 - if (enableTransitionTracing) {
3027 - if (options !== undefined && options.name !== undefined) {
3028 - currentTransition.name = options.name;
3029 - currentTransition.startTime = now();
3030 - }
3031 - }
3032 -
3033 - if (__DEV__) {
3034 - currentTransition._updatedFibers = new Set();
3035 - }
3036 -
3038 try {
3039 const returnValue = callback();
3040 const onStartTransitionFinish = ReactSharedInternals.S;
packages/react-reconciler/src/ReactFiberLane.js
+1 -1
@@ -8,7 +8,7 @@
8 */
9
10 import type {Fiber, FiberRoot} from './ReactInternalTypes';
11 -import type {Transition} from './ReactFiberTracingMarkerComponent';
11 +import type {Transition} from 'react/src/ReactStartTransition';
12 import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates';
13
14 // TODO: Ideally these types would be opaque but that doesn't work well with
packages/react-reconciler/src/ReactFiberRootScheduler.js
+2 -2
@@ -10,7 +10,7 @@
10 import type {FiberRoot} from './ReactInternalTypes';
11 import type {Lane, Lanes} from './ReactFiberLane';
12 import type {PriorityLevel} from 'scheduler/src/SchedulerPriorities';
13 -import type {BatchConfigTransition} from './ReactFiberTracingMarkerComponent';
13 +import type {Transition} from 'react/src/ReactStartTransition';
14
15 import {
16 disableLegacyMode,
@@ -635,7 +635,7 @@ export function requestTransitionLane(
635 // This argument isn't used, it's only here to encourage the caller to
636 // check that it's inside a transition before calling this function.
637 // TODO: Make this non-nullable. Requires a tweak to useOptimistic.
638 - transition: BatchConfigTransition | null,
638 + transition: Transition | null,
639 ): Lane {
640 // The algorithm for assigning an update to a lane should be stable for all
641 // updates at the same priority within the same event. To do this, the
packages/react-reconciler/src/ReactFiberTracingMarkerComponent.js
+48 -44
@@ -12,6 +12,7 @@ import type {
12 Fiber,
13 FiberRoot,
14 } from './ReactInternalTypes';
15 +import type {Transition} from 'react/src/ReactStartTransition';
16 import type {OffscreenInstance} from './ReactFiberActivityComponent';
17 import type {StackCursor} from './ReactFiberStack';
18
@@ -36,19 +37,6 @@ export type PendingTransitionCallbacks = {
37 markerComplete: Map<string, Set<Transition>> | null,
38 };
39
39 -// TODO: Unclear to me why these are separate types
40 -export type Transition = {
41 - name: string,
42 - startTime: number,
43 - ...
44 -};
45 -
46 -export type BatchConfigTransition = {
47 - name?: string,
48 - startTime?: number,
49 - _updatedFibers?: Set<Fiber>,
50 -};
51 -
40 // TODO: Is there a way to not include the tag or name here?
41 export type TracingMarkerInstance = {
42 tag?: TracingMarkerTag,
@@ -79,9 +67,11 @@ export function processTransitionCallbacks(
67 const transitionStart = pendingTransitions.transitionStart;
68 const onTransitionStart = callbacks.onTransitionStart;
69 if (transitionStart !== null && onTransitionStart != null) {
82 - transitionStart.forEach(transition =>
83 - onTransitionStart(transition.name, transition.startTime),
84 - );
70 + transitionStart.forEach(transition => {
71 + if (transition.name != null) {
72 + onTransitionStart(transition.name, transition.startTime);
73 + }
74 + });
75 }
76
77 const markerProgress = pendingTransitions.markerProgress;
@@ -95,13 +85,15 @@ export function processTransitionCallbacks(
85 ? Array.from(markerInstance.pendingBoundaries.values())
86 : [];
87 markerInstance.transitions.forEach(transition => {
98 - onMarkerProgress(
99 - transition.name,
100 - markerName,
101 - transition.startTime,
102 - endTime,
103 - pending,
104 - );
88 + if (transition.name != null) {
89 + onMarkerProgress(
90 + transition.name,
91 + markerName,
92 + transition.startTime,
93 + endTime,
94 + pending,
95 + );
96 + }
97 });
98 }
99 });
@@ -112,12 +104,14 @@ export function processTransitionCallbacks(
104 if (markerComplete !== null && onMarkerComplete != null) {
105 markerComplete.forEach((transitions, markerName) => {
106 transitions.forEach(transition => {
115 - onMarkerComplete(
116 - transition.name,
117 - markerName,
118 - transition.startTime,
119 - endTime,
120 - );
107 + if (transition.name != null) {
108 + onMarkerComplete(
109 + transition.name,
110 + markerName,
111 + transition.startTime,
112 + endTime,
113 + );
114 + }
115 });
116 });
117 }
@@ -153,12 +147,14 @@ export function processTransitionCallbacks(
147 });
148
149 if (filteredAborts.length > 0) {
156 - onMarkerIncomplete(
157 - transition.name,
158 - markerName,
159 - transition.startTime,
160 - filteredAborts,
161 - );
150 + if (transition.name != null) {
151 + onMarkerIncomplete(
152 + transition.name,
153 + markerName,
154 + transition.startTime,
155 + filteredAborts,
156 + );
157 + }
158 }
159 });
160 });
@@ -168,21 +164,29 @@ export function processTransitionCallbacks(
164 const onTransitionProgress = callbacks.onTransitionProgress;
165 if (onTransitionProgress != null && transitionProgress !== null) {
166 transitionProgress.forEach((pending, transition) => {
171 - onTransitionProgress(
172 - transition.name,
173 - transition.startTime,
174 - endTime,
175 - Array.from(pending.values()),
176 - );
167 + if (transition.name != null) {
168 + onTransitionProgress(
169 + transition.name,
170 + transition.startTime,
171 + endTime,
172 + Array.from(pending.values()),
173 + );
174 + }
175 });
176 }
177
178 const transitionComplete = pendingTransitions.transitionComplete;
179 const onTransitionComplete = callbacks.onTransitionComplete;
180 if (transitionComplete !== null && onTransitionComplete != null) {
183 - transitionComplete.forEach(transition =>
184 - onTransitionComplete(transition.name, transition.startTime, endTime),
185 - );
181 + transitionComplete.forEach(transition => {
182 + if (transition.name != null) {
183 + onTransitionComplete(
184 + transition.name,
185 + transition.startTime,
186 + endTime,
187 + );
188 + }
189 + });
190 }
191 }
192 }
packages/react-reconciler/src/ReactFiberTransition.js
+3 -6
@@ -11,10 +11,7 @@ import type {Thenable} from 'shared/ReactTypes';
11 import type {Lanes} from './ReactFiberLane';
12 import type {StackCursor} from './ReactFiberStack';
13 import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent';
14 -import type {
15 - BatchConfigTransition,
16 - Transition,
17 -} from './ReactFiberTracingMarkerComponent';
14 +import type {Transition} from 'react/src/ReactStartTransition';
15
16 import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
17 import {isPrimaryRenderer} from './ReactFiberConfig';
@@ -57,7 +54,7 @@ export const NoTransition = null;
54 // reconciler. Leaving this for a future PR.
55 const prevOnStartTransitionFinish = ReactSharedInternals.S;
56 ReactSharedInternals.S = function onStartTransitionFinishForReconciler(
60 - transition: BatchConfigTransition,
57 + transition: Transition,
58 returnValue: mixed,
59 ) {
60 if (
@@ -81,7 +78,7 @@ ReactSharedInternals.S = function onStartTransitionFinishForReconciler(
78 }
79 };
80
84 -export function requestCurrentTransition(): BatchConfigTransition | null {
81 +export function requestCurrentTransition(): Transition | null {
82 return ReactSharedInternals.T;
83 }
84
packages/react-reconciler/src/ReactFiberWorkLoop.js
+1 -3
@@ -14,10 +14,10 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes';
14 import type {Lanes, Lane} from './ReactFiberLane';
15 import type {SuspenseState} from './ReactFiberSuspenseComponent';
16 import type {FunctionComponentUpdateQueue} from './ReactFiberHooks';
17 +import type {Transition} from 'react/src/ReactStartTransition';
18 import type {
19 PendingTransitionCallbacks,
20 PendingBoundaries,
20 - Transition,
21 TransitionAbort,
22 } from './ReactFiberTracingMarkerComponent';
23 import type {OffscreenInstance} from './ReactFiberActivityComponent';
@@ -927,8 +927,6 @@ export function scheduleUpdateOnFiber(
927 transition.startTime = now();
928 }
929
930 - // $FlowFixMe[prop-missing]: The BatchConfigTransition and Transition types are incompatible but was previously untyped and thus uncaught
931 - // $FlowFixMe[incompatible-call]: "
930 addTransitionToLanesMap(root, transition, lane);
931 }
932 }
packages/react-reconciler/src/ReactInternalTypes.js
+2 -4
@@ -33,10 +33,8 @@ import type {
33 TransitionStatus,
34 } from './ReactFiberConfig';
35 import type {Cache} from './ReactFiberCacheComponent';
36 -import type {
37 - TracingMarkerInstance,
38 - Transition,
39 -} from './ReactFiberTracingMarkerComponent';
36 +import type {Transition} from 'react/src/ReactStartTransition';
37 +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
38 import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates';
39 import type {ComponentStackNode} from 'react-server/src/ReactFizzComponentStack';
40 import type {ThenableState} from './ReactFiberThenable';
packages/react/src/ReactSharedInternalsClient.js
+3 -3
@@ -9,14 +9,14 @@
9
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
12 -import type {BatchConfigTransition} from 'react-reconciler/src/ReactFiberTracingMarkerComponent';
12 +import type {Transition} from './ReactStartTransition';
13 import type {TransitionTypes} from './ReactTransitionType';
14
15 export type SharedStateClient = {
16 H: null | Dispatcher, // ReactCurrentDispatcher for Hooks
17 A: null | AsyncDispatcher, // ReactCurrentCache for Cache
18 - T: null | BatchConfigTransition, // ReactCurrentBatchConfig for Transitions
19 - S: null | ((BatchConfigTransition, mixed) => void), // onStartTransitionFinish
18 + T: null | Transition, // ReactCurrentBatchConfig for Transitions
19 + S: null | ((Transition, mixed) => void), // onStartTransitionFinish
20 V: null | TransitionTypes, // Pending Transition Types for the Next Transition
21
22 // DEV-only
packages/react/src/ReactStartTransition.js
+18 -13
@@ -6,8 +6,9 @@
6 *
7 * @flow
8 */
9 -import type {BatchConfigTransition} from 'react-reconciler/src/ReactFiberTracingMarkerComponent';
9 +
10 import type {StartTransitionOptions} from 'shared/ReactTypes';
11 +import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
12
13 import ReactSharedInternals from 'shared/ReactSharedInternals';
14
@@ -15,24 +16,28 @@ import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
16
17 import reportGlobalError from 'shared/reportGlobalError';
18
19 +export type Transition = {
20 + name: null | string, // enableTransitionTracing only
21 + startTime: number, // enableTransitionTracing only
22 + _updatedFibers: Set<Fiber>, // DEV-only
23 + ...
24 +};
25 +
26 export function startTransition(
27 scope: () => void,
28 options?: StartTransitionOptions,
29 ) {
30 const prevTransition = ReactSharedInternals.T;
23 - const currentTransition: BatchConfigTransition = {};
24 - ReactSharedInternals.T = currentTransition;
25 -
31 + const currentTransition: Transition = ({}: any);
32 + if (enableTransitionTracing) {
33 + currentTransition.name =
34 + options !== undefined && options.name !== undefined ? options.name : null;
35 + currentTransition.startTime = -1; // TODO: This should read the timestamp.
36 + }
37 if (__DEV__) {
38 currentTransition._updatedFibers = new Set();
39 }
29 -
30 - if (enableTransitionTracing) {
31 - if (options !== undefined && options.name !== undefined) {
32 - currentTransition.name = options.name;
33 - currentTransition.startTime = -1;
34 - }
35 - }
40 + ReactSharedInternals.T = currentTransition;
41
42 try {
43 const returnValue = scope();
@@ -56,8 +61,8 @@ export function startTransition(
61 }
62
63 function warnAboutTransitionSubscriptions(
59 - prevTransition: BatchConfigTransition | null,
60 - currentTransition: BatchConfigTransition,
64 + prevTransition: Transition | null,
65 + currentTransition: Transition,
66 ) {
67 if (__DEV__) {
68 if (prevTransition === null && currentTransition._updatedFibers) {