Skip to content

Commit 21c9cf4

Browse files
committed
Keep track of how many async startTransition are currently running
If it's zero, we can warn if you try to call addTransitionType.
1 parent b058656 commit 21c9cf4

File tree

4 files changed

+50
-0
lines changed

4 files changed

+50
-0
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

+16
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,11 @@ function handleActionReturnValue<S, P>(
22192219
typeof returnValue.then === 'function'
22202220
) {
22212221
const thenable = ((returnValue: any): Thenable<Awaited<S>>);
2222+
if (__DEV__) {
2223+
// Keep track of the number of async transitions still running so we can warn.
2224+
ReactSharedInternals.asyncTransitions++;
2225+
thenable.then(releaseAsyncTransition, releaseAsyncTransition);
2226+
}
22222227
// Attach a listener to read the return state of the action. As soon as
22232228
// this resolves, we can run the next action in the sequence.
22242229
thenable.then(
@@ -3026,6 +3031,12 @@ function updateDeferredValueImpl<T>(
30263031
}
30273032
}
30283033

3034+
function releaseAsyncTransition() {
3035+
if (__DEV__) {
3036+
ReactSharedInternals.asyncTransitions--;
3037+
}
3038+
}
3039+
30293040
function startTransition<S>(
30303041
fiber: Fiber,
30313042
queue: UpdateQueue<S | Thenable<S>, BasicStateAction<S | Thenable<S>>>,
@@ -3083,6 +3094,11 @@ function startTransition<S>(
30833094
typeof returnValue.then === 'function'
30843095
) {
30853096
const thenable = ((returnValue: any): Thenable<mixed>);
3097+
if (__DEV__) {
3098+
// Keep track of the number of async transitions still running so we can warn.
3099+
ReactSharedInternals.asyncTransitions++;
3100+
thenable.then(releaseAsyncTransition, releaseAsyncTransition);
3101+
}
30863102
// Create a thenable that resolves to `finishedState` once the async
30873103
// action has completed.
30883104
const thenableForFinishedState = chainThenableValue(

packages/react/src/ReactSharedInternalsClient.js

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export type SharedStateClient = {
3939
// ReactCurrentActQueue
4040
actQueue: null | Array<RendererTask>,
4141

42+
// When zero this means we're outside an async startTransition.
43+
asyncTransitions: number,
44+
4245
// Used to reproduce behavior of `batchedUpdates` in legacy mode.
4346
isBatchingLegacy: boolean,
4447
didScheduleLegacyUpdate: boolean,
@@ -75,6 +78,7 @@ if (enableViewTransition) {
7578

7679
if (__DEV__) {
7780
ReactSharedInternals.actQueue = null;
81+
ReactSharedInternals.asyncTransitions = 0;
7882
ReactSharedInternals.isBatchingLegacy = false;
7983
ReactSharedInternals.didScheduleLegacyUpdate = false;
8084
ReactSharedInternals.didUsePromise = false;

packages/react/src/ReactStartTransition.js

+11
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ export type Transition = {
3737
...
3838
};
3939

40+
function releaseAsyncTransition() {
41+
if (__DEV__) {
42+
ReactSharedInternals.asyncTransitions--;
43+
}
44+
}
45+
4046
export function startTransition(
4147
scope: () => void,
4248
options?: StartTransitionOptions,
@@ -67,6 +73,11 @@ export function startTransition(
6773
returnValue !== null &&
6874
typeof returnValue.then === 'function'
6975
) {
76+
if (__DEV__) {
77+
// Keep track of the number of async transitions still running so we can warn.
78+
ReactSharedInternals.asyncTransitions++;
79+
returnValue.then(releaseAsyncTransition, releaseAsyncTransition);
80+
}
7081
returnValue.then(noop, reportGlobalError);
7182
}
7283
} catch (error) {

packages/react/src/ReactTransitionType.js

+19
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ export function addTransitionType(type: string): void {
4545
pendingTransitionTypes = pendingGestureTransitionTypes = [];
4646
}
4747
} else {
48+
if (__DEV__) {
49+
if (
50+
ReactSharedInternals.T === null &&
51+
ReactSharedInternals.asyncTransitions === 0
52+
) {
53+
if (enableGestureTransition) {
54+
console.error(
55+
'addTransitionType can only be called inside a `startTransition()` ' +
56+
'or `startGestureTransition()` callback. ' +
57+
'It must be associated with a specific Transition.',
58+
);
59+
} else {
60+
console.error(
61+
'addTransitionType can only be called inside a `startTransition()` ' +
62+
'callback. It must be associated with a specific Transition.',
63+
);
64+
}
65+
}
66+
}
4867
// Otherwise we're either inside a synchronous startTransition
4968
// or in the async gap of one, which we track globally.
5069
pendingTransitionTypes = ReactSharedInternals.V;

0 commit comments

Comments
 (0)