Skip to content

Commit 54e88ed

Browse files
authored
Bugfix: Flush legacy sync passive effects at beginning of event (#21846)
* Re-land recent flushSync changes Adds back #21776 and #21775, which were removed due to an internal e2e test failure. Will attempt to fix in subsequent commits. * Failing test: Legacy mode sync passive effects In concurrent roots, if a render is synchronous, we flush its passive effects synchronously. In legacy roots, we don't do this because all updates are synchronous — so we need to flush at the beginning of the next event. This is how `discreteUpdates` worked. * Flush legacy passive effects at beginning of event Fixes test added in previous commit.
1 parent cb8afda commit 54e88ed

17 files changed

+184
-337
lines changed

packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap

+14-14
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Object {
4040
6 => 1,
4141
},
4242
"passiveEffectDuration": null,
43-
"priorityLevel": "Normal",
43+
"priorityLevel": "Immediate",
4444
"timestamp": 16,
4545
"updaters": Array [
4646
Object {
@@ -87,7 +87,7 @@ Object {
8787
4 => 2,
8888
},
8989
"passiveEffectDuration": null,
90-
"priorityLevel": "Normal",
90+
"priorityLevel": "Immediate",
9191
"timestamp": 15,
9292
"updaters": Array [
9393
Object {
@@ -186,7 +186,7 @@ Object {
186186
6 => 1,
187187
},
188188
"passiveEffectDuration": null,
189-
"priorityLevel": "Normal",
189+
"priorityLevel": "Immediate",
190190
"timestamp": 12,
191191
"updaters": Array [
192192
Object {
@@ -445,7 +445,7 @@ Object {
445445
],
446446
],
447447
"passiveEffectDuration": null,
448-
"priorityLevel": "Normal",
448+
"priorityLevel": "Immediate",
449449
"timestamp": 12,
450450
"updaters": Array [
451451
Object {
@@ -938,7 +938,7 @@ Object {
938938
],
939939
],
940940
"passiveEffectDuration": null,
941-
"priorityLevel": "Normal",
941+
"priorityLevel": "Immediate",
942942
"timestamp": 11,
943943
"updaters": Array [
944944
Object {
@@ -1597,7 +1597,7 @@ Object {
15971597
17 => 1,
15981598
},
15991599
"passiveEffectDuration": null,
1600-
"priorityLevel": "Normal",
1600+
"priorityLevel": "Immediate",
16011601
"timestamp": 24,
16021602
"updaters": Array [
16031603
Object {
@@ -1687,7 +1687,7 @@ Object {
16871687
"fiberActualDurations": Map {},
16881688
"fiberSelfDurations": Map {},
16891689
"passiveEffectDuration": 0,
1690-
"priorityLevel": "Normal",
1690+
"priorityLevel": "Immediate",
16911691
"timestamp": 34,
16921692
"updaters": Array [
16931693
Object {
@@ -2223,7 +2223,7 @@ Object {
22232223
],
22242224
],
22252225
"passiveEffectDuration": null,
2226-
"priorityLevel": "Normal",
2226+
"priorityLevel": "Immediate",
22272227
"timestamp": 24,
22282228
"updaters": Array [
22292229
Object {
@@ -2310,7 +2310,7 @@ Object {
23102310
"fiberActualDurations": Array [],
23112311
"fiberSelfDurations": Array [],
23122312
"passiveEffectDuration": 0,
2313-
"priorityLevel": "Normal",
2313+
"priorityLevel": "Immediate",
23142314
"timestamp": 34,
23152315
"updaters": Array [
23162316
Object {
@@ -2431,7 +2431,7 @@ Object {
24312431
2 => 0,
24322432
},
24332433
"passiveEffectDuration": null,
2434-
"priorityLevel": "Normal",
2434+
"priorityLevel": "Immediate",
24352435
"timestamp": 0,
24362436
"updaters": Array [
24372437
Object {
@@ -2506,7 +2506,7 @@ Object {
25062506
3 => 0,
25072507
},
25082508
"passiveEffectDuration": 0,
2509-
"priorityLevel": "Normal",
2509+
"priorityLevel": "Immediate",
25102510
"timestamp": 0,
25112511
"updaters": Array [
25122512
Object {
@@ -2715,7 +2715,7 @@ Object {
27152715
],
27162716
],
27172717
"passiveEffectDuration": 0,
2718-
"priorityLevel": "Normal",
2718+
"priorityLevel": "Immediate",
27192719
"timestamp": 0,
27202720
"updaters": Array [
27212721
Object {
@@ -3071,7 +3071,7 @@ Object {
30713071
7 => 0,
30723072
},
30733073
"passiveEffectDuration": null,
3074-
"priorityLevel": "Normal",
3074+
"priorityLevel": "Immediate",
30753075
"timestamp": 0,
30763076
"updaters": Array [
30773077
Object {
@@ -3515,7 +3515,7 @@ Object {
35153515
],
35163516
],
35173517
"passiveEffectDuration": null,
3518-
"priorityLevel": "Normal",
3518+
"priorityLevel": "Immediate",
35193519
"timestamp": 0,
35203520
"updaters": Array [
35213521
Object {

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

+1-7
Original file line numberDiff line numberDiff line change
@@ -1154,19 +1154,13 @@ describe('ReactDOMFiber', () => {
11541154
expect(ops).toEqual(['A']);
11551155

11561156
if (__DEV__) {
1157-
const errorCalls = console.error.calls.count();
1157+
expect(console.error.calls.count()).toBe(2);
11581158
expect(console.error.calls.argsFor(0)[0]).toMatch(
11591159
'ReactDOM.render is no longer supported in React 18',
11601160
);
11611161
expect(console.error.calls.argsFor(1)[0]).toMatch(
11621162
'ReactDOM.render is no longer supported in React 18',
11631163
);
1164-
// TODO: this warning shouldn't be firing in the first place if user didn't call it.
1165-
for (let i = 2; i < errorCalls; i++) {
1166-
expect(console.error.calls.argsFor(i)[0]).toMatch(
1167-
'unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.',
1168-
);
1169-
}
11701164
}
11711165
});
11721166

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ describe('ReactMount', () => {
277277
expect(calls).toBe(5);
278278
});
279279

280-
it('initial mount is sync inside batchedUpdates, but task work is deferred until the end of the batch', () => {
280+
it('initial mount of legacy root is sync inside batchedUpdates, as if it were wrapped in flushSync', () => {
281281
const container1 = document.createElement('div');
282282
const container2 = document.createElement('div');
283283

@@ -302,12 +302,12 @@ describe('ReactMount', () => {
302302

303303
// Initial mount on another root. Should flush immediately.
304304
ReactDOM.render(<Foo>a</Foo>, container2);
305-
// The update did not flush yet.
306-
expect(container1.textContent).toEqual('1');
307-
// The initial mount flushed, but not the update scheduled in cDM.
308-
expect(container2.textContent).toEqual('a');
305+
// The earlier update also flushed, since flushSync flushes all pending
306+
// sync work across all roots.
307+
expect(container1.textContent).toEqual('2');
308+
// Layout updates are also flushed synchronously
309+
expect(container2.textContent).toEqual('a!');
309310
});
310-
// All updates have flushed.
311311
expect(container1.textContent).toEqual('2');
312312
expect(container2.textContent).toEqual('a!');
313313
});

packages/react-dom/src/client/ReactDOM.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import {createEventHandle} from './ReactDOMEventHandle';
2323
import {
2424
batchedUpdates,
2525
discreteUpdates,
26-
flushDiscreteUpdates,
2726
flushSync,
27+
flushSyncWithoutWarningIfAlreadyRendering,
2828
flushControlled,
2929
injectIntoDevTools,
3030
attemptSynchronousHydration,
@@ -100,7 +100,7 @@ setRestoreImplementation(restoreControlledState);
100100
setBatchingImplementation(
101101
batchedUpdates,
102102
discreteUpdates,
103-
flushDiscreteUpdates,
103+
flushSyncWithoutWarningIfAlreadyRendering,
104104
);
105105

106106
function createPortal(

packages/react-dom/src/client/ReactDOMLegacy.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
createContainer,
3030
findHostInstanceWithNoPortals,
3131
updateContainer,
32-
unbatchedUpdates,
32+
flushSyncWithoutWarningIfAlreadyRendering,
3333
getPublicRootInstance,
3434
findHostInstance,
3535
findHostInstanceWithWarning,
@@ -174,7 +174,7 @@ function legacyRenderSubtreeIntoContainer(
174174
};
175175
}
176176
// Initial mount should not be batched.
177-
unbatchedUpdates(() => {
177+
flushSyncWithoutWarningIfAlreadyRendering(() => {
178178
updateContainer(children, fiberRoot, parentComponent, callback);
179179
});
180180
} else {
@@ -357,7 +357,7 @@ export function unmountComponentAtNode(container: Container) {
357357
}
358358

359359
// Unmount should not be batched.
360-
unbatchedUpdates(() => {
360+
flushSyncWithoutWarningIfAlreadyRendering(() => {
361361
legacyRenderSubtreeIntoContainer(null, null, container, false, () => {
362362
// $FlowFixMe This should probably use `delete container._reactRootContainer`
363363
container._reactRootContainer = null;

packages/react-dom/src/events/ReactDOMUpdateBatching.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let batchedUpdatesImpl = function(fn, bookkeeping) {
2323
let discreteUpdatesImpl = function(fn, a, b, c, d) {
2424
return fn(a, b, c, d);
2525
};
26-
let flushDiscreteUpdatesImpl = function() {};
26+
let flushSyncImpl = function() {};
2727

2828
let isInsideEventHandler = false;
2929

@@ -39,7 +39,7 @@ function finishEventHandler() {
3939
// bails out of the update without touching the DOM.
4040
// TODO: Restore state in the microtask, after the discrete updates flush,
4141
// instead of early flushing them here.
42-
flushDiscreteUpdatesImpl();
42+
flushSyncImpl();
4343
restoreStateIfNeeded();
4444
}
4545
}
@@ -67,9 +67,9 @@ export function discreteUpdates(fn, a, b, c, d) {
6767
export function setBatchingImplementation(
6868
_batchedUpdatesImpl,
6969
_discreteUpdatesImpl,
70-
_flushDiscreteUpdatesImpl,
70+
_flushSyncImpl,
7171
) {
7272
batchedUpdatesImpl = _batchedUpdatesImpl;
7373
discreteUpdatesImpl = _discreteUpdatesImpl;
74-
flushDiscreteUpdatesImpl = _flushDiscreteUpdatesImpl;
74+
flushSyncImpl = _flushSyncImpl;
7575
}

packages/react-noop-renderer/src/ReactNoop.js

-2
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,8 @@ export const {
3838
flushExpired,
3939
batchedUpdates,
4040
deferredUpdates,
41-
unbatchedUpdates,
4241
discreteUpdates,
4342
idleUpdates,
44-
flushDiscreteUpdates,
4543
flushSync,
4644
flushPassiveEffects,
4745
act,

packages/react-noop-renderer/src/ReactNoopPersistent.js

-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export const {
3838
flushExpired,
3939
batchedUpdates,
4040
deferredUpdates,
41-
unbatchedUpdates,
4241
discreteUpdates,
4342
idleUpdates,
4443
flushDiscreteUpdates,

packages/react-noop-renderer/src/createReactNoop.js

-4
Original file line numberDiff line numberDiff line change
@@ -901,8 +901,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
901901

902902
deferredUpdates: NoopRenderer.deferredUpdates,
903903

904-
unbatchedUpdates: NoopRenderer.unbatchedUpdates,
905-
906904
discreteUpdates: NoopRenderer.discreteUpdates,
907905

908906
idleUpdates<T>(fn: () => T): T {
@@ -915,8 +913,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
915913
}
916914
},
917915

918-
flushDiscreteUpdates: NoopRenderer.flushDiscreteUpdates,
919-
920916
flushSync(fn: () => mixed) {
921917
NoopRenderer.flushSync(fn);
922918
},

packages/react-reconciler/src/ReactFiberReconciler.js

+5-10
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ import {
1818
createContainer as createContainer_old,
1919
updateContainer as updateContainer_old,
2020
batchedUpdates as batchedUpdates_old,
21-
unbatchedUpdates as unbatchedUpdates_old,
2221
deferredUpdates as deferredUpdates_old,
2322
discreteUpdates as discreteUpdates_old,
24-
flushDiscreteUpdates as flushDiscreteUpdates_old,
2523
flushControlled as flushControlled_old,
2624
flushSync as flushSync_old,
25+
flushSyncWithoutWarningIfAlreadyRendering as flushSyncWithoutWarningIfAlreadyRendering_old,
2726
flushPassiveEffects as flushPassiveEffects_old,
2827
getPublicRootInstance as getPublicRootInstance_old,
2928
attemptSynchronousHydration as attemptSynchronousHydration_old,
@@ -56,12 +55,11 @@ import {
5655
createContainer as createContainer_new,
5756
updateContainer as updateContainer_new,
5857
batchedUpdates as batchedUpdates_new,
59-
unbatchedUpdates as unbatchedUpdates_new,
6058
deferredUpdates as deferredUpdates_new,
6159
discreteUpdates as discreteUpdates_new,
62-
flushDiscreteUpdates as flushDiscreteUpdates_new,
6360
flushControlled as flushControlled_new,
6461
flushSync as flushSync_new,
62+
flushSyncWithoutWarningIfAlreadyRendering as flushSyncWithoutWarningIfAlreadyRendering_new,
6563
flushPassiveEffects as flushPassiveEffects_new,
6664
getPublicRootInstance as getPublicRootInstance_new,
6765
attemptSynchronousHydration as attemptSynchronousHydration_new,
@@ -99,22 +97,19 @@ export const updateContainer = enableNewReconciler
9997
export const batchedUpdates = enableNewReconciler
10098
? batchedUpdates_new
10199
: batchedUpdates_old;
102-
export const unbatchedUpdates = enableNewReconciler
103-
? unbatchedUpdates_new
104-
: unbatchedUpdates_old;
105100
export const deferredUpdates = enableNewReconciler
106101
? deferredUpdates_new
107102
: deferredUpdates_old;
108103
export const discreteUpdates = enableNewReconciler
109104
? discreteUpdates_new
110105
: discreteUpdates_old;
111-
export const flushDiscreteUpdates = enableNewReconciler
112-
? flushDiscreteUpdates_new
113-
: flushDiscreteUpdates_old;
114106
export const flushControlled = enableNewReconciler
115107
? flushControlled_new
116108
: flushControlled_old;
117109
export const flushSync = enableNewReconciler ? flushSync_new : flushSync_old;
110+
export const flushSyncWithoutWarningIfAlreadyRendering = enableNewReconciler
111+
? flushSyncWithoutWarningIfAlreadyRendering_new
112+
: flushSyncWithoutWarningIfAlreadyRendering_old;
118113
export const flushPassiveEffects = enableNewReconciler
119114
? flushPassiveEffects_new
120115
: flushPassiveEffects_old;

packages/react-reconciler/src/ReactFiberReconciler.new.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,11 @@ import {
5252
scheduleUpdateOnFiber,
5353
flushRoot,
5454
batchedUpdates,
55-
unbatchedUpdates,
5655
flushSync,
5756
flushControlled,
5857
deferredUpdates,
5958
discreteUpdates,
60-
flushDiscreteUpdates,
59+
flushSyncWithoutWarningIfAlreadyRendering,
6160
flushPassiveEffects,
6261
} from './ReactFiberWorkLoop.new';
6362
import {
@@ -327,12 +326,11 @@ export function updateContainer(
327326

328327
export {
329328
batchedUpdates,
330-
unbatchedUpdates,
331329
deferredUpdates,
332330
discreteUpdates,
333-
flushDiscreteUpdates,
334331
flushControlled,
335332
flushSync,
333+
flushSyncWithoutWarningIfAlreadyRendering,
336334
flushPassiveEffects,
337335
};
338336

packages/react-reconciler/src/ReactFiberReconciler.old.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,11 @@ import {
5252
scheduleUpdateOnFiber,
5353
flushRoot,
5454
batchedUpdates,
55-
unbatchedUpdates,
5655
flushSync,
5756
flushControlled,
5857
deferredUpdates,
5958
discreteUpdates,
60-
flushDiscreteUpdates,
59+
flushSyncWithoutWarningIfAlreadyRendering,
6160
flushPassiveEffects,
6261
} from './ReactFiberWorkLoop.old';
6362
import {
@@ -327,12 +326,11 @@ export function updateContainer(
327326

328327
export {
329328
batchedUpdates,
330-
unbatchedUpdates,
331329
deferredUpdates,
332330
discreteUpdates,
333-
flushDiscreteUpdates,
334331
flushControlled,
335332
flushSync,
333+
flushSyncWithoutWarningIfAlreadyRendering,
336334
flushPassiveEffects,
337335
};
338336

0 commit comments

Comments
 (0)