Skip to content

Commit 5227402

Browse files
committed
Collect Transition Types on the Transition instance
Conceptually they belong to each Transition object and should only be applied where updates to that batch are applied. However, since we batch regular Transitions together they go into the same set.
1 parent f57175d commit 5227402

7 files changed

+71
-69
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

+7
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
enableLegacyCache,
4343
disableLegacyMode,
4444
enableNoCloningMemoCache,
45+
enableViewTransition,
4546
enableGestureTransition,
4647
} from 'shared/ReactFeatureFlags';
4748
import {
@@ -2159,6 +2160,9 @@ function runActionStateAction<S, P>(
21592160
// This is a fork of startTransition
21602161
const prevTransition = ReactSharedInternals.T;
21612162
const currentTransition: Transition = ({}: any);
2163+
if (enableViewTransition) {
2164+
currentTransition.types = null;
2165+
}
21622166
if (enableGestureTransition) {
21632167
currentTransition.gesture = null;
21642168
}
@@ -3052,6 +3056,9 @@ function startTransition<S>(
30523056

30533057
const prevTransition = ReactSharedInternals.T;
30543058
const currentTransition: Transition = ({}: any);
3059+
if (enableViewTransition) {
3060+
currentTransition.types = null;
3061+
}
30553062
if (enableGestureTransition) {
30563063
currentTransition.gesture = null;
30573064
}

packages/react-reconciler/src/ReactFiberTransition.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type {StackCursor} from './ReactFiberStack';
1717
import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent';
1818
import type {Transition} from 'react/src/ReactStartTransition';
1919
import type {ScheduledGesture} from './ReactFiberGestureScheduler';
20-
import type {TransitionTypes} from 'react/src/ReactTransitionType';
2120

2221
import {
2322
enableTransitionTracing,
@@ -34,6 +33,7 @@ import {
3433
retainCache,
3534
CacheContext,
3635
} from './ReactFiberCacheComponent';
36+
import {queueTransitionTypes} from './ReactFiberTransitionTypes';
3737

3838
import ReactSharedInternals from 'shared/ReactSharedInternals';
3939
import {entangleAsyncAction} from './ReactFiberAsyncAction';
@@ -87,6 +87,7 @@ ReactSharedInternals.S = function onStartTransitionFinishForReconciler(
8787
const thenable: Thenable<mixed> = (returnValue: any);
8888
entangleAsyncAction(transition, thenable);
8989
}
90+
queueTransitionTypes(transition.types);
9091
if (prevOnStartTransitionFinish !== null) {
9192
prevOnStartTransitionFinish(transition, returnValue);
9293
}
@@ -113,15 +114,13 @@ if (enableGestureTransition) {
113114
transition: Transition,
114115
provider: GestureProvider,
115116
options: ?GestureOptions,
116-
transitionTypes: null | TransitionTypes,
117117
): () => void {
118118
let cancel = null;
119119
if (prevOnStartGestureTransitionFinish !== null) {
120120
cancel = prevOnStartGestureTransitionFinish(
121121
transition,
122122
provider,
123123
options,
124-
transitionTypes,
125124
);
126125
}
127126
// For every root that has work scheduled, check if there's a ScheduledGesture
@@ -138,7 +137,7 @@ if (enableGestureTransition) {
138137
root,
139138
provider,
140139
options,
141-
transitionTypes,
140+
transition.types,
142141
);
143142
if (scheduledGesture !== null) {
144143
cancel = chainGestureCancellation(root, scheduledGesture, cancel);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 {TransitionTypes} from 'react/src/ReactTransitionType';
11+
12+
import {enableViewTransition} from 'shared/ReactFeatureFlags';
13+
14+
let queuedTransitionTypes: null | TransitionTypes = null;
15+
16+
export function queueTransitionTypes(
17+
transitionTypes: null | TransitionTypes,
18+
): void {
19+
// Currently, we assume that all Transitions are batched together into a global single commit.
20+
if (enableViewTransition && transitionTypes !== null) {
21+
let queued = queuedTransitionTypes;
22+
if (queued === null) {
23+
queued = queuedTransitionTypes = [];
24+
}
25+
for (let i = 0; i < transitionTypes.length; i++) {
26+
const transitionType = transitionTypes[i];
27+
if (queued.indexOf(transitionType) === -1) {
28+
queued.push(transitionType);
29+
}
30+
}
31+
}
32+
}
33+
34+
export function claimQueuedTransitionTypes(): null | TransitionTypes {
35+
const claimed = queuedTransitionTypes;
36+
queuedTransitionTypes = null;
37+
return claimed;
38+
}

packages/react-reconciler/src/ReactFiberWorkLoop.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ import {
358358
deleteScheduledGesture,
359359
stopCompletedGestures,
360360
} from './ReactFiberGestureScheduler';
361+
import {claimQueuedTransitionTypes} from './ReactFiberTransitionTypes';
361362

362363
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
363364

@@ -3404,11 +3405,7 @@ function commitRoot(
34043405
pendingViewTransitionEvents = null;
34053406
if (includesOnlyViewTransitionEligibleLanes(lanes)) {
34063407
// Claim any pending Transition Types for this commit.
3407-
// This means that multiple roots committing independent View Transitions
3408-
// 1) end up staggered because we can only have one at a time.
3409-
// 2) only the first one gets all the Transition Types.
3410-
pendingTransitionTypes = ReactSharedInternals.V;
3411-
ReactSharedInternals.V = null;
3408+
pendingTransitionTypes = claimQueuedTransitionTypes();
34123409
passiveSubtreeMask = PassiveTransitionMask;
34133410
} else {
34143411
pendingTransitionTypes = null;

packages/react/src/ReactSharedInternalsClient.js

+1-10
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,15 @@
1010
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
1111
import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
1212
import type {Transition} from './ReactStartTransition';
13-
import type {TransitionTypes} from './ReactTransitionType';
1413
import type {GestureProvider, GestureOptions} from 'shared/ReactTypes';
1514

16-
import {
17-
enableViewTransition,
18-
enableGestureTransition,
19-
} from 'shared/ReactFeatureFlags';
15+
import {enableGestureTransition} from 'shared/ReactFeatureFlags';
2016

2117
type onStartTransitionFinish = (Transition, mixed) => void;
2218
type onStartGestureTransitionFinish = (
2319
Transition,
2420
GestureProvider,
2521
?GestureOptions,
26-
transitionTypes: null | TransitionTypes,
2722
) => () => void;
2823

2924
export type SharedStateClient = {
@@ -32,7 +27,6 @@ export type SharedStateClient = {
3227
T: null | Transition, // ReactCurrentBatchConfig for Transitions
3328
S: null | onStartTransitionFinish,
3429
G: null | onStartGestureTransitionFinish,
35-
V: null | TransitionTypes, // Pending Transition Types for the Next Transition
3630

3731
// DEV-only
3832

@@ -72,9 +66,6 @@ const ReactSharedInternals: SharedStateClient = ({
7266
if (enableGestureTransition) {
7367
ReactSharedInternals.G = null;
7468
}
75-
if (enableViewTransition) {
76-
ReactSharedInternals.V = null;
77-
}
7869

7970
if (__DEV__) {
8071
ReactSharedInternals.actQueue = null;

packages/react/src/ReactStartTransition.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,20 @@ import type {
1313
GestureProvider,
1414
GestureOptions,
1515
} from 'shared/ReactTypes';
16+
import type {TransitionTypes} from './ReactTransitionType';
1617

1718
import ReactSharedInternals from 'shared/ReactSharedInternals';
1819

1920
import {
2021
enableTransitionTracing,
22+
enableViewTransition,
2123
enableGestureTransition,
2224
} from 'shared/ReactFeatureFlags';
2325

24-
import {
25-
pendingGestureTransitionTypes,
26-
pushPendingGestureTransitionTypes,
27-
popPendingGestureTransitionTypes,
28-
} from './ReactTransitionType';
29-
3026
import reportGlobalError from 'shared/reportGlobalError';
3127

3228
export type Transition = {
29+
types: null | TransitionTypes, // enableViewTransition
3330
gesture: null | GestureProvider, // enableGestureTransition
3431
name: null | string, // enableTransitionTracing only
3532
startTime: number, // enableTransitionTracing only
@@ -49,6 +46,9 @@ export function startTransition(
4946
): void {
5047
const prevTransition = ReactSharedInternals.T;
5148
const currentTransition: Transition = ({}: any);
49+
if (enableViewTransition) {
50+
currentTransition.types = null;
51+
}
5252
if (enableGestureTransition) {
5353
currentTransition.gesture = null;
5454
}
@@ -109,6 +109,9 @@ export function startGestureTransition(
109109
}
110110
const prevTransition = ReactSharedInternals.T;
111111
const currentTransition: Transition = ({}: any);
112+
if (enableViewTransition) {
113+
currentTransition.types = null;
114+
}
112115
if (enableGestureTransition) {
113116
currentTransition.gesture = provider;
114117
}
@@ -122,8 +125,6 @@ export function startGestureTransition(
122125
}
123126
ReactSharedInternals.T = currentTransition;
124127

125-
const prevTransitionTypes = pushPendingGestureTransitionTypes();
126-
127128
try {
128129
const returnValue = scope();
129130
if (__DEV__) {
@@ -137,20 +138,17 @@ export function startGestureTransition(
137138
);
138139
}
139140
}
140-
const transitionTypes = pendingGestureTransitionTypes;
141141
const onStartGestureTransitionFinish = ReactSharedInternals.G;
142142
if (onStartGestureTransitionFinish !== null) {
143143
return onStartGestureTransitionFinish(
144144
currentTransition,
145145
provider,
146146
options,
147-
transitionTypes,
148147
);
149148
}
150149
} catch (error) {
151150
reportGlobalError(error);
152151
} finally {
153-
popPendingGestureTransitionTypes(prevTransitionTypes);
154152
ReactSharedInternals.T = prevTransition;
155153
}
156154
return function cancelGesture() {

packages/react/src/ReactTransitionType.js

+11-39
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,24 @@ import {
1212
enableViewTransition,
1313
enableGestureTransition,
1414
} from 'shared/ReactFeatureFlags';
15+
import {startTransition} from './ReactStartTransition';
1516

1617
export type TransitionTypes = Array<string>;
1718

18-
// This one is only available synchronously so we don't need to use ReactSharedInternals
19-
// for this state. Instead, we track it in isomorphic and pass it to the renderer.
20-
export let pendingGestureTransitionTypes: null | TransitionTypes = null;
21-
22-
export function pushPendingGestureTransitionTypes(): null | TransitionTypes {
23-
const prev = pendingGestureTransitionTypes;
24-
pendingGestureTransitionTypes = null;
25-
return prev;
26-
}
27-
28-
export function popPendingGestureTransitionTypes(
29-
prev: null | TransitionTypes,
30-
): void {
31-
pendingGestureTransitionTypes = prev;
32-
}
33-
3419
export function addTransitionType(type: string): void {
3520
if (enableViewTransition) {
36-
let pendingTransitionTypes: null | TransitionTypes;
37-
if (
38-
enableGestureTransition &&
39-
ReactSharedInternals.T !== null &&
40-
ReactSharedInternals.T.gesture !== null
41-
) {
42-
// We're inside a startGestureTransition which is always sync.
43-
pendingTransitionTypes = pendingGestureTransitionTypes;
44-
if (pendingTransitionTypes === null) {
45-
pendingTransitionTypes = pendingGestureTransitionTypes = [];
21+
const transition = ReactSharedInternals.T;
22+
if (transition !== null) {
23+
const transitionTypes = transition.types;
24+
if (transitionTypes === null) {
25+
transition.types = [type];
26+
} else if (transitionTypes.indexOf(type) === -1) {
27+
transitionTypes.push(type);
4628
}
4729
} else {
30+
// We're in the async gap. Simulate an implicit startTransition around it.
4831
if (__DEV__) {
49-
if (
50-
ReactSharedInternals.T === null &&
51-
ReactSharedInternals.asyncTransitions === 0
52-
) {
32+
if (ReactSharedInternals.asyncTransitions === 0) {
5333
if (enableGestureTransition) {
5434
console.error(
5535
'addTransitionType can only be called inside a `startTransition()` ' +
@@ -64,15 +44,7 @@ export function addTransitionType(type: string): void {
6444
}
6545
}
6646
}
67-
// Otherwise we're either inside a synchronous startTransition
68-
// or in the async gap of one, which we track globally.
69-
pendingTransitionTypes = ReactSharedInternals.V;
70-
if (pendingTransitionTypes === null) {
71-
pendingTransitionTypes = ReactSharedInternals.V = [];
72-
}
73-
}
74-
if (pendingTransitionTypes.indexOf(type) === -1) {
75-
pendingTransitionTypes.push(type);
47+
startTransition(addTransitionType.bind(null, type));
7648
}
7749
}
7850
}

0 commit comments

Comments
 (0)