diff --git a/packages/react-reconciler/src/__tests__/ReactInterleavedUpdates-test.js b/packages/react-reconciler/src/__tests__/ReactInterleavedUpdates-test.js
index c9e66fe0399e..153d4d28bd3f 100644
--- a/packages/react-reconciler/src/__tests__/ReactInterleavedUpdates-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactInterleavedUpdates-test.js
@@ -140,4 +140,52 @@ describe('ReactInterleavedUpdates', () => {
expect(Scheduler).toHaveYielded([2, 2, 2]);
expect(root).toMatchRenderedOutput('222');
});
+
+ test('regression for #24350: does not add to main update queue until interleaved update queue has been cleared', async () => {
+ let setStep;
+ function App() {
+ const [step, _setState] = useState(0);
+ setStep = _setState;
+ return (
+ <>
+
+
+
+ >
+ );
+ }
+
+ const root = ReactNoop.createRoot();
+ await act(async () => {
+ root.render();
+ });
+ expect(Scheduler).toHaveYielded(['A0', 'B0', 'C0']);
+ expect(root).toMatchRenderedOutput('A0B0C0');
+
+ await act(async () => {
+ // Start the render phase.
+ startTransition(() => {
+ setStep(1);
+ });
+ expect(Scheduler).toFlushAndYieldThrough(['A1', 'B1']);
+
+ // Schedule an interleaved update. This gets placed on a special queue.
+ startTransition(() => {
+ setStep(2);
+ });
+
+ // Finish rendering the first update.
+ expect(Scheduler).toFlushUntilNextPaint(['C1']);
+
+ // Schedule another update. (In the regression case, this was treated
+ // as a normal, non-interleaved update and it was inserted into the queue
+ // before the interleaved one was processed.)
+ startTransition(() => {
+ setStep(3);
+ });
+ });
+ // The last update should win.
+ expect(Scheduler).toHaveYielded(['A3', 'B3', 'C3']);
+ expect(root).toMatchRenderedOutput('A3B3C3');
+ });
});