diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
index 41d237f30ac60..82e5bfc3fac00 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
@@ -586,6 +586,63 @@ describe('ReactHooksWithNoopRenderer', () => {
root.render();
expect(Scheduler).toFlushAndYield(['Suspend!']);
});
+
+ it('discards render phase updates if something suspends, but not other updates in the same component', async () => {
+ const thenable = {then() {}};
+ function Foo({signal}) {
+ return (
+
+
+
+ );
+ }
+
+ let setLabel;
+ function Bar({signal: newSignal}) {
+ let [counter, setCounter] = useState(0);
+ let [signal, setSignal] = useState(true);
+
+ // Increment a counter every time the signal changes
+ if (signal !== newSignal) {
+ setCounter(c => c + 1);
+ setSignal(newSignal);
+ if (counter === 0) {
+ // We're suspending during a render that includes render phase
+ // updates. Those updates should not persist to the next render.
+ Scheduler.unstable_yieldValue('Suspend!');
+ throw thenable;
+ }
+ }
+
+ let [label, _setLabel] = useState('A');
+ setLabel = _setLabel;
+
+ return ;
+ }
+
+ const root = ReactNoop.createRoot();
+ root.render();
+
+ expect(Scheduler).toFlushAndYield(['A:0']);
+ expect(root).toMatchRenderedOutput();
+
+ await ReactNoop.act(async () => {
+ root.render();
+ setLabel('B');
+ });
+ expect(Scheduler).toHaveYielded(['Suspend!']);
+ expect(root).toMatchRenderedOutput();
+
+ // Rendering again should suspend again.
+ root.render();
+ expect(Scheduler).toFlushAndYield(['Suspend!']);
+
+ // Flip the signal back to "cancel" the update. However, the update to
+ // label should still proceed. It shouldn't have been dropped.
+ root.render();
+ expect(Scheduler).toFlushAndYield(['B:0']);
+ expect(root).toMatchRenderedOutput();
+ });
});
describe('useReducer', () => {