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) {