Skip to content

Commit ac040cc

Browse files
committed
Failing test: Updates "un-committed" when rebasing
Adds a failing test case where an update that was committed is later skipped over during a rebase. This should never happen.
1 parent 54f6673 commit ac040cc

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,4 +653,128 @@ describe('ReactIncrementalUpdates', () => {
653653
expect(Scheduler).toFlushAndYield(['Commit: goodbye']);
654654
});
655655
});
656+
657+
it('when rebasing, does not exclude updates that were already committed, regardless of priority', async () => {
658+
const {useState, useLayoutEffect} = React;
659+
660+
let pushToLog;
661+
function App() {
662+
const [log, setLog] = useState('');
663+
pushToLog = msg => {
664+
setLog(prevLog => prevLog + msg);
665+
};
666+
667+
useLayoutEffect(
668+
() => {
669+
Scheduler.unstable_yieldValue('Committed: ' + log);
670+
if (log === 'B') {
671+
// Right after B commits, schedule additional updates.
672+
Scheduler.unstable_runWithPriority(
673+
Scheduler.unstable_UserBlockingPriority,
674+
() => {
675+
pushToLog('C');
676+
},
677+
);
678+
setLog(prevLog => prevLog + 'D');
679+
}
680+
},
681+
[log],
682+
);
683+
684+
return log;
685+
}
686+
687+
const root = ReactNoop.createRoot();
688+
await ReactNoop.act(async () => {
689+
root.render(<App />);
690+
});
691+
expect(Scheduler).toHaveYielded(['Committed: ']);
692+
expect(root).toMatchRenderedOutput('');
693+
694+
await ReactNoop.act(async () => {
695+
pushToLog('A');
696+
Scheduler.unstable_runWithPriority(
697+
Scheduler.unstable_UserBlockingPriority,
698+
() => {
699+
pushToLog('B');
700+
},
701+
);
702+
});
703+
expect(Scheduler).toHaveYielded([
704+
// A and B are pending. B is higher priority, so we'll render that first.
705+
'Committed: B',
706+
// Because A comes first in the queue, we're now in rebase mode. B must
707+
// be rebased on top of A. Also, in a layout effect, we received two new
708+
// updates: C and D. C is user-blocking and D is synchronous.
709+
//
710+
// First render the synchronous update. What we're testing here is that
711+
// B *is not dropped* even though it has lower than sync priority. That's
712+
// because we already committed it. However, this render should not
713+
// include C, because that update wasn't already committed.
714+
'Committed: BD',
715+
'Committed: BCD',
716+
'Committed: ABCD',
717+
]);
718+
expect(root).toMatchRenderedOutput('ABCD');
719+
});
720+
721+
it('when rebasing, does not exclude updates that were already committed, regardless of priority (classes)', async () => {
722+
let pushToLog;
723+
class App extends React.Component {
724+
state = {log: ''};
725+
pushToLog = msg => {
726+
this.setState(prevState => ({log: prevState.log + msg}));
727+
};
728+
componentDidUpdate() {
729+
Scheduler.unstable_yieldValue('Committed: ' + this.state.log);
730+
if (this.state.log === 'B') {
731+
// Right after B commits, schedule additional updates.
732+
Scheduler.unstable_runWithPriority(
733+
Scheduler.unstable_UserBlockingPriority,
734+
() => {
735+
this.pushToLog('C');
736+
},
737+
);
738+
this.pushToLog('D');
739+
}
740+
}
741+
render() {
742+
pushToLog = this.pushToLog;
743+
return this.state.log;
744+
}
745+
}
746+
747+
const root = ReactNoop.createRoot();
748+
await ReactNoop.act(async () => {
749+
root.render(<App />);
750+
});
751+
expect(Scheduler).toHaveYielded([]);
752+
expect(root).toMatchRenderedOutput('');
753+
754+
await ReactNoop.act(async () => {
755+
pushToLog('A');
756+
Scheduler.unstable_runWithPriority(
757+
Scheduler.unstable_UserBlockingPriority,
758+
() => {
759+
pushToLog('B');
760+
},
761+
);
762+
});
763+
expect(Scheduler).toHaveYielded([
764+
// A and B are pending. B is higher priority, so we'll render that first.
765+
'Committed: B',
766+
// Because A comes first in the queue, we're now in rebase mode. B must
767+
// be rebased on top of A. Also, in a layout effect, we received two new
768+
// updates: C and D. C is user-blocking and D is synchronous.
769+
//
770+
// First render the synchronous update. What we're testing here is that
771+
// B *is not dropped* even though it has lower than sync priority. That's
772+
// because we already committed it. However, this render should not
773+
// include C, because that update wasn't already committed.
774+
'Committed: BD',
775+
'Committed: BCD',
776+
'Committed: ABCD',
777+
]);
778+
expect(root).toMatchRenderedOutput('ABCD');
779+
});
656780
});

0 commit comments

Comments
 (0)