mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Fiber] Fix infinite loop in scheduler and add more tests (#8172)
* Make test more complete * Add a failing test for scheduling in reverse order It hangs forever because we don't clear next pointer when unscheduling a root. Therefore, when it's scheduled again, it brings all its previous chain with it, potentially creating a cycle. * Clear the next pointer when unscheduling a root Fixes a potential infinite cycle when we reschedule a root. * Add new tests to Fiber test tracker
This commit is contained in:
@@ -794,6 +794,11 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalReflection-test.js
|
||||
* handles isMounted when an unmount is deferred
|
||||
* finds no node before insertion and correct node before deletion
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
|
||||
* schedules multiple roots
|
||||
* works on deferred roots in the order they were scheduled
|
||||
* works on roots with animation priority before deferred work
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
|
||||
* can update child nodes of a host instance
|
||||
* can update child nodes of a fragment
|
||||
|
||||
@@ -77,14 +77,22 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
|
||||
function findNextUnitOfWork() {
|
||||
// Clear out roots with no more work on them.
|
||||
while (nextScheduledRoot && nextScheduledRoot.current.pendingWorkPriority === NoWork) {
|
||||
// Unschedule this root.
|
||||
nextScheduledRoot.isScheduled = false;
|
||||
// Read the next pointer now.
|
||||
// We need to clear it in case this root gets scheduled again later.
|
||||
const next = nextScheduledRoot.nextScheduledRoot;
|
||||
nextScheduledRoot.nextScheduledRoot = null;
|
||||
// Exit if we cleared all the roots and there's no work to do.
|
||||
if (nextScheduledRoot === lastScheduledRoot) {
|
||||
nextScheduledRoot = null;
|
||||
lastScheduledRoot = null;
|
||||
nextPriorityLevel = NoWork;
|
||||
return null;
|
||||
}
|
||||
nextScheduledRoot = nextScheduledRoot.nextScheduledRoot;
|
||||
// Continue with the next root.
|
||||
// If there's no work on it, it will get unscheduled too.
|
||||
nextScheduledRoot = next;
|
||||
}
|
||||
let root = nextScheduledRoot;
|
||||
let highestPriorityRoot = null;
|
||||
|
||||
@@ -70,6 +70,36 @@ describe('ReactIncrementalScheduling', () => {
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:6')]);
|
||||
});
|
||||
|
||||
it('works on deferred roots in the order they were scheduled', () => {
|
||||
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
|
||||
// Schedule deferred work in the reverse order
|
||||
ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
|
||||
ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
|
||||
// Ensure it starts in the order it was scheduled
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
// Schedule last bit of work, it will get processed the last
|
||||
ReactNoop.renderToRootWithID(<span prop="a:2" />, 'a');
|
||||
// Keep performing work in the order it was scheduled
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
});
|
||||
|
||||
it('works on roots with animation priority before deferred work', () => {
|
||||
ReactNoop.renderToRootWithID(<span prop="a:1" />, 'a');
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
@@ -85,7 +115,8 @@ describe('ReactIncrementalScheduling', () => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:2" />, 'b');
|
||||
ReactNoop.renderToRootWithID(<span prop="c:2" />, 'c');
|
||||
});
|
||||
// Handle roots with animation work first
|
||||
// We're flushing deferred work
|
||||
// Still, roots with animation work are handled first
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
|
||||
@@ -94,10 +125,26 @@ describe('ReactIncrementalScheduling', () => {
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
// Then handle deferred root
|
||||
|
||||
// More deferred and animation work just got scheduled!
|
||||
ReactNoop.renderToRootWithID(<span prop="c:3" />, 'c');
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:3" />, 'b');
|
||||
});
|
||||
// Animation is still handled first
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
|
||||
// Finally we handle deferred root in the order it was scheduled
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:2')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:2')]);
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:2')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user