Skip to content

Commit 6efa3c0

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 f3c782b commit 6efa3c0

File tree

4 files changed

+45
-0
lines changed

4 files changed

+45
-0
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

+11
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,12 @@ function updateDeferredValueImpl<T>(
30263026
}
30273027
}
30283028

3029+
function releaseAsyncTransition() {
3030+
if (__DEV__) {
3031+
ReactSharedInternals.asyncTransitions--;
3032+
}
3033+
}
3034+
30293035
function startTransition<S>(
30303036
fiber: Fiber,
30313037
queue: UpdateQueue<S | Thenable<S>, BasicStateAction<S | Thenable<S>>>,
@@ -3083,6 +3089,11 @@ function startTransition<S>(
30833089
typeof returnValue.then === 'function'
30843090
) {
30853091
const thenable = ((returnValue: any): Thenable<mixed>);
3092+
if (__DEV__) {
3093+
// Keep track of the number of async transitions still running so we can warn.
3094+
ReactSharedInternals.asyncTransitions++;
3095+
thenable.then(releaseAsyncTransition, releaseAsyncTransition);
3096+
}
30863097
// Create a thenable that resolves to `finishedState` once the async
30873098
// action has completed.
30883099
const thenableForFinishedState = chainThenableValue(

packages/react/src/ReactSharedInternalsClient.js

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

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

7578
if (__DEV__) {
7679
ReactSharedInternals.actQueue = null;
80+
ReactSharedInternals.asyncTransitions = 0;
7781
ReactSharedInternals.isBatchingLegacy = false;
7882
ReactSharedInternals.didScheduleLegacyUpdate = false;
7983
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)