diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index 7c04351d52..b232fb0b22 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -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 diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index 4c7b0c5154..b334d12924 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -77,14 +77,22 @@ module.exports = function(config : HostConfig) { 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; diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js index 4987ff390e..58ddcedd7d 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js @@ -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(, 'a'); + ReactNoop.renderToRootWithID(, 'b'); + ReactNoop.renderToRootWithID(, '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(, 'c'); + ReactNoop.renderToRootWithID(, '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(, '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(, 'a'); ReactNoop.renderToRootWithID(, 'b'); @@ -85,7 +115,8 @@ describe('ReactIncrementalScheduling', () => { ReactNoop.renderToRootWithID(, 'b'); ReactNoop.renderToRootWithID(, '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(, 'c'); + ReactNoop.performAnimationWork(() => { + ReactNoop.renderToRootWithID(, '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')]); }); });