Skip to content

Commit bd16b96

Browse files
committed
Async action support for React.startTransition (#28097)
This adds support for async actions to the "isomorphic" version of startTransition (i.e. the one exported by the "react" package). Previously, async actions were only supported by the startTransition that is returned from the useTransition hook. The interesting part about the isomorphic startTransition is that it's not associated with any particular root. It must work with updates to arbitrary roots, or even arbitrary React renderers in the same app. (For example, both React DOM and React Three Fiber.) The idea is that React.startTransition should behave as if every root had an implicit useTransition hook, and you composed together all the startTransitions provided by those hooks. Multiple updates to the same root will be batched together. However, updates to one root will not be batched with updates to other roots. Features like useOptimistic work the same as with the hook version. There is one difference from from the hook version of startTransition: an error triggered inside an async action cannot be captured by an error boundary, because it's not associated with any particular part of the tree. You should handle errors the same way you would in a regular event, e.g. with a global error event handler, or with a local `try/catch`. DiffTrain build for [85b296e](85b296e)
1 parent b9b97d9 commit bd16b96

26 files changed

+1300
-762
lines changed

compiled/facebook-www/REVISION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
382190c595126837ee7e43b4fa953f4edd30e01c
1+
85b296e9b6ded4accd9ec3389297f95091fb1ac0

compiled/facebook-www/React-dev.classic.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ if (__DEV__) {
2424
) {
2525
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2626
}
27-
var ReactVersion = "18.3.0-www-classic-92f5a9ab";
27+
var ReactVersion = "18.3.0-www-classic-af9ffe5b";
2828

2929
// ATTENTION
3030
// When adding new symbols to this file,
@@ -2961,8 +2961,14 @@ if (__DEV__) {
29612961
}
29622962

29632963
function startTransition(scope, options) {
2964-
var prevTransition = ReactCurrentBatchConfig.transition;
2965-
ReactCurrentBatchConfig.transition = {};
2964+
var prevTransition = ReactCurrentBatchConfig.transition; // Each renderer registers a callback to receive the return value of
2965+
// the scope function. This is used to implement async actions.
2966+
2967+
var callbacks = new Set();
2968+
var transition = {
2969+
_callbacks: callbacks
2970+
};
2971+
ReactCurrentBatchConfig.transition = transition;
29662972
var currentTransition = ReactCurrentBatchConfig.transition;
29672973

29682974
{
@@ -2979,7 +2985,10 @@ if (__DEV__) {
29792985
}
29802986

29812987
try {
2982-
scope();
2988+
var returnValue = scope();
2989+
callbacks.forEach(function (callback) {
2990+
return callback(currentTransition, returnValue);
2991+
});
29832992
} finally {
29842993
ReactCurrentBatchConfig.transition = prevTransition;
29852994

compiled/facebook-www/React-dev.modern.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ if (__DEV__) {
2424
) {
2525
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2626
}
27-
var ReactVersion = "18.3.0-www-modern-c131adbc";
27+
var ReactVersion = "18.3.0-www-modern-c761065d";
2828

2929
// ATTENTION
3030
// When adding new symbols to this file,
@@ -2926,8 +2926,14 @@ if (__DEV__) {
29262926
}
29272927

29282928
function startTransition(scope, options) {
2929-
var prevTransition = ReactCurrentBatchConfig.transition;
2930-
ReactCurrentBatchConfig.transition = {};
2929+
var prevTransition = ReactCurrentBatchConfig.transition; // Each renderer registers a callback to receive the return value of
2930+
// the scope function. This is used to implement async actions.
2931+
2932+
var callbacks = new Set();
2933+
var transition = {
2934+
_callbacks: callbacks
2935+
};
2936+
ReactCurrentBatchConfig.transition = transition;
29312937
var currentTransition = ReactCurrentBatchConfig.transition;
29322938

29332939
{
@@ -2944,7 +2950,10 @@ if (__DEV__) {
29442950
}
29452951

29462952
try {
2947-
scope();
2953+
var returnValue = scope();
2954+
callbacks.forEach(function (callback) {
2955+
return callback(currentTransition, returnValue);
2956+
});
29482957
} finally {
29492958
ReactCurrentBatchConfig.transition = prevTransition;
29502959

compiled/facebook-www/React-prod.classic.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -444,15 +444,20 @@ exports.memo = function (type, compare) {
444444
};
445445
};
446446
exports.startTransition = function (scope, options) {
447-
var prevTransition = ReactCurrentBatchConfig.transition;
448-
ReactCurrentBatchConfig.transition = {};
447+
var prevTransition = ReactCurrentBatchConfig.transition,
448+
callbacks = new Set();
449+
ReactCurrentBatchConfig.transition = { _callbacks: callbacks };
450+
var currentTransition = ReactCurrentBatchConfig.transition;
449451
enableTransitionTracing &&
450452
void 0 !== options &&
451453
void 0 !== options.name &&
452454
((ReactCurrentBatchConfig.transition.name = options.name),
453455
(ReactCurrentBatchConfig.transition.startTime = -1));
454456
try {
455-
scope();
457+
var returnValue = scope();
458+
callbacks.forEach(function (callback) {
459+
return callback(currentTransition, returnValue);
460+
});
456461
} finally {
457462
ReactCurrentBatchConfig.transition = prevTransition;
458463
}
@@ -546,4 +551,4 @@ exports.useSyncExternalStore = function (
546551
exports.useTransition = function () {
547552
return ReactCurrentDispatcher.current.useTransition();
548553
};
549-
exports.version = "18.3.0-www-classic-63557ef1";
554+
exports.version = "18.3.0-www-classic-d6e83430";

compiled/facebook-www/React-prod.modern.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -437,15 +437,20 @@ exports.memo = function (type, compare) {
437437
};
438438
};
439439
exports.startTransition = function (scope, options) {
440-
var prevTransition = ReactCurrentBatchConfig.transition;
441-
ReactCurrentBatchConfig.transition = {};
440+
var prevTransition = ReactCurrentBatchConfig.transition,
441+
callbacks = new Set();
442+
ReactCurrentBatchConfig.transition = { _callbacks: callbacks };
443+
var currentTransition = ReactCurrentBatchConfig.transition;
442444
enableTransitionTracing &&
443445
void 0 !== options &&
444446
void 0 !== options.name &&
445447
((ReactCurrentBatchConfig.transition.name = options.name),
446448
(ReactCurrentBatchConfig.transition.startTime = -1));
447449
try {
448-
scope();
450+
var returnValue = scope();
451+
callbacks.forEach(function (callback) {
452+
return callback(currentTransition, returnValue);
453+
});
449454
} finally {
450455
ReactCurrentBatchConfig.transition = prevTransition;
451456
}
@@ -538,4 +543,4 @@ exports.useSyncExternalStore = function (
538543
exports.useTransition = function () {
539544
return ReactCurrentDispatcher.current.useTransition();
540545
};
541-
exports.version = "18.3.0-www-modern-14d3332b";
546+
exports.version = "18.3.0-www-modern-5a0946d0";

compiled/facebook-www/React-profiling.classic.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -448,15 +448,20 @@ exports.memo = function (type, compare) {
448448
};
449449
};
450450
exports.startTransition = function (scope, options) {
451-
var prevTransition = ReactCurrentBatchConfig.transition;
452-
ReactCurrentBatchConfig.transition = {};
451+
var prevTransition = ReactCurrentBatchConfig.transition,
452+
callbacks = new Set();
453+
ReactCurrentBatchConfig.transition = { _callbacks: callbacks };
454+
var currentTransition = ReactCurrentBatchConfig.transition;
453455
enableTransitionTracing &&
454456
void 0 !== options &&
455457
void 0 !== options.name &&
456458
((ReactCurrentBatchConfig.transition.name = options.name),
457459
(ReactCurrentBatchConfig.transition.startTime = -1));
458460
try {
459-
scope();
461+
var returnValue = scope();
462+
callbacks.forEach(function (callback) {
463+
return callback(currentTransition, returnValue);
464+
});
460465
} finally {
461466
ReactCurrentBatchConfig.transition = prevTransition;
462467
}
@@ -550,7 +555,7 @@ exports.useSyncExternalStore = function (
550555
exports.useTransition = function () {
551556
return ReactCurrentDispatcher.current.useTransition();
552557
};
553-
exports.version = "18.3.0-www-classic-de2439d8";
558+
exports.version = "18.3.0-www-classic-00275770";
554559
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
555560
"function" ===
556561
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,20 @@ exports.memo = function (type, compare) {
441441
};
442442
};
443443
exports.startTransition = function (scope, options) {
444-
var prevTransition = ReactCurrentBatchConfig.transition;
445-
ReactCurrentBatchConfig.transition = {};
444+
var prevTransition = ReactCurrentBatchConfig.transition,
445+
callbacks = new Set();
446+
ReactCurrentBatchConfig.transition = { _callbacks: callbacks };
447+
var currentTransition = ReactCurrentBatchConfig.transition;
446448
enableTransitionTracing &&
447449
void 0 !== options &&
448450
void 0 !== options.name &&
449451
((ReactCurrentBatchConfig.transition.name = options.name),
450452
(ReactCurrentBatchConfig.transition.startTime = -1));
451453
try {
452-
scope();
454+
var returnValue = scope();
455+
callbacks.forEach(function (callback) {
456+
return callback(currentTransition, returnValue);
457+
});
453458
} finally {
454459
ReactCurrentBatchConfig.transition = prevTransition;
455460
}
@@ -542,7 +547,7 @@ exports.useSyncExternalStore = function (
542547
exports.useTransition = function () {
543548
return ReactCurrentDispatcher.current.useTransition();
544549
};
545-
exports.version = "18.3.0-www-modern-5b70880a";
550+
exports.version = "18.3.0-www-modern-a0181100";
546551
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
547552
"function" ===
548553
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)