Skip to content

Commit f923179

Browse files
committed
Revert "Revert "Allow transitions to interrupt Suspensey commits (#26531)""
This reverts commit cfd9d8c.
1 parent d08b6cf commit f923179

File tree

4 files changed

+40
-65
lines changed

4 files changed

+40
-65
lines changed

packages/react-dom/src/__tests__/ReactDOMFloat-test.js

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3216,7 +3216,7 @@ body {
32163216
);
32173217
});
32183218

3219-
it('can start a new suspended commit after a previous one finishes', async () => {
3219+
it('can interrupt a suspended commit with a new transition', async () => {
32203220
function App({children}) {
32213221
return (
32223222
<html>
@@ -3225,81 +3225,66 @@ body {
32253225
);
32263226
}
32273227
const root = ReactDOMClient.createRoot(document);
3228-
root.render(<App />);
3228+
root.render(<App>(empty)</App>);
3229+
3230+
// Start a transition to "A"
32293231
React.startTransition(() => {
32303232
root.render(
32313233
<App>
3232-
hello
3233-
<link rel="stylesheet" href="foo" precedence="default" />
3234+
A
3235+
<link rel="stylesheet" href="A" precedence="default" />
32343236
</App>,
32353237
);
32363238
});
32373239
await waitForAll([]);
3240+
3241+
// "A" hasn't loaded yet, so we remain on the initial UI. Its preload
3242+
// has been inserted into the head, though.
32383243
expect(getMeaningfulChildren(document)).toEqual(
32393244
<html>
32403245
<head>
3241-
<link rel="preload" href="foo" as="style" />
3246+
<link rel="preload" href="A" as="style" />
32423247
</head>
3243-
<body />
3248+
<body>(empty)</body>
32443249
</html>,
32453250
);
32463251

3252+
// Interrupt the "A" transition with a new one, "B"
32473253
React.startTransition(() => {
32483254
root.render(
32493255
<App>
3250-
hello2
3251-
{null}
3252-
<link rel="stylesheet" href="bar" precedence="default" />
3256+
B
3257+
<link rel="stylesheet" href="B" precedence="default" />
32533258
</App>,
32543259
);
32553260
});
32563261
await waitForAll([]);
3257-
expect(getMeaningfulChildren(document)).toEqual(
3258-
<html>
3259-
<head>
3260-
<link rel="preload" href="foo" as="style" />
3261-
</head>
3262-
<body />
3263-
</html>,
3264-
);
32653262

3266-
loadPreloads();
3267-
loadStylesheets();
3268-
assertLog(['load preload: foo', 'load stylesheet: foo']);
3263+
// Still on the initial UI because "B" hasn't loaded, but its preload
3264+
// is now in the head, too.
32693265
expect(getMeaningfulChildren(document)).toEqual(
32703266
<html>
32713267
<head>
3272-
<link rel="stylesheet" href="foo" data-precedence="default" />
3273-
<link rel="preload" href="foo" as="style" />
3268+
<link rel="preload" href="A" as="style" />
3269+
<link rel="preload" href="B" as="style" />
32743270
</head>
3275-
<body>hello</body>
3271+
<body>(empty)</body>
32763272
</html>,
32773273
);
32783274

3279-
// The second update should process now
3280-
await waitForAll([]);
3281-
expect(getMeaningfulChildren(document)).toEqual(
3282-
<html>
3283-
<head>
3284-
<link rel="stylesheet" href="foo" data-precedence="default" />
3285-
<link rel="preload" href="foo" as="style" />
3286-
<link rel="preload" href="bar" as="style" />
3287-
</head>
3288-
<body>hello</body>
3289-
</html>,
3290-
);
3275+
// Finish loading
32913276
loadPreloads();
32923277
loadStylesheets();
3293-
assertLog(['load preload: bar', 'load stylesheet: bar']);
3278+
assertLog(['load preload: A', 'load preload: B', 'load stylesheet: B']);
3279+
// The "B" transition has finished.
32943280
expect(getMeaningfulChildren(document)).toEqual(
32953281
<html>
32963282
<head>
3297-
<link rel="stylesheet" href="foo" data-precedence="default" />
3298-
<link rel="stylesheet" href="bar" data-precedence="default" />
3299-
<link rel="preload" href="foo" as="style" />
3300-
<link rel="preload" href="bar" as="style" />
3283+
<link rel="stylesheet" href="B" data-precedence="default" />
3284+
<link rel="preload" href="A" as="style" />
3285+
<link rel="preload" href="B" as="style" />
33013286
</head>
3302-
<body>hello2</body>
3287+
<body>B</body>
33033288
</html>,
33043289
);
33053290
});

packages/react-reconciler/src/ReactFiberRootScheduler.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
SyncLane,
1919
getHighestPriorityLane,
2020
getNextLanes,
21-
includesOnlyNonUrgentLanes,
2221
includesSyncLane,
2322
markStarvedLanesAsExpired,
2423
} from './ReactFiberLane';
@@ -301,14 +300,16 @@ function scheduleTaskForRootDuringMicrotask(
301300

302301
const existingCallbackNode = root.callbackNode;
303302
if (
303+
// Check if there's nothing to work on
304304
nextLanes === NoLanes ||
305305
// If this root is currently suspended and waiting for data to resolve, don't
306306
// schedule a task to render it. We'll either wait for a ping, or wait to
307307
// receive an update.
308-
(isWorkLoopSuspendedOnData() && root === workInProgressRoot) ||
309-
// We should only interrupt a pending commit if the new update
310-
// is urgent.
311-
(root.cancelPendingCommit !== null && includesOnlyNonUrgentLanes(nextLanes))
308+
//
309+
// Suspended render phase
310+
(root === workInProgressRoot && isWorkLoopSuspendedOnData()) ||
311+
// Suspended commit phase
312+
root.cancelPendingCommit !== null
312313
) {
313314
// Fast path: There's nothing to work on.
314315
if (existingCallbackNode !== null) {

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,8 +725,11 @@ export function scheduleUpdateOnFiber(
725725
// Check if the work loop is currently suspended and waiting for data to
726726
// finish loading.
727727
if (
728-
workInProgressSuspendedReason === SuspendedOnData &&
729-
root === workInProgressRoot
728+
// Suspended render phase
729+
(root === workInProgressRoot &&
730+
workInProgressSuspendedReason === SuspendedOnData) ||
731+
// Suspended commit phase
732+
root.cancelPendingCommit !== null
730733
) {
731734
// The incoming update might unblock the current render. Interrupt the
732735
// current attempt and restart from the top.

packages/react-reconciler/src/__tests__/ReactSuspenseyCommitPhase-test.js

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ describe('ReactSuspenseyCommitPhase', () => {
137137
// Nothing showing yet.
138138
expect(root).toMatchRenderedOutput(null);
139139

140-
// If there's an urgent update, it should interrupt the suspended commit.
140+
// If there's an update, it should interrupt the suspended commit.
141141
await act(() => {
142142
root.render(<Text text="Something else" />);
143143
});
144144
assertLog(['Something else']);
145145
expect(root).toMatchRenderedOutput('Something else');
146146
});
147147

148-
test('a non-urgent update does not interrupt a suspended commit', async () => {
148+
test('a transition update interrupts a suspended commit', async () => {
149149
const root = ReactNoop.createRoot();
150150

151151
// Mount an image. This transition will suspend because it's not inside a
@@ -159,26 +159,12 @@ describe('ReactSuspenseyCommitPhase', () => {
159159
// Nothing showing yet.
160160
expect(root).toMatchRenderedOutput(null);
161161

162-
// If there's another transition update, it should not interrupt the
163-
// suspended commit.
162+
// If there's an update, it should interrupt the suspended commit.
164163
await act(() => {
165164
startTransition(() => {
166165
root.render(<Text text="Something else" />);
167166
});
168167
});
169-
// Still suspended.
170-
expect(root).toMatchRenderedOutput(null);
171-
172-
await act(() => {
173-
// Resolving the image should result in an immediate, synchronous commit.
174-
resolveSuspenseyThing('A');
175-
expect(root).toMatchRenderedOutput(<suspensey-thing src="A" />);
176-
});
177-
// Then the second transition is unblocked.
178-
// TODO: Right now the only way to unsuspend a commit early is to proceed
179-
// with the commit even if everything isn't ready. Maybe there should also
180-
// be a way to abort a commit so that it can be interrupted by
181-
// another transition.
182168
assertLog(['Something else']);
183169
expect(root).toMatchRenderedOutput('Something else');
184170
});

0 commit comments

Comments
 (0)