diff --git a/scripts/fiber/tests-failing.txt b/scripts/fiber/tests-failing.txt index 098d4df39e1..ed2077e38cb 100644 --- a/scripts/fiber/tests-failing.txt +++ b/scripts/fiber/tests-failing.txt @@ -72,6 +72,9 @@ src/renderers/art/__tests__/ReactART-test.js * resolves refs before componentDidMount * resolves refs before componentDidUpdate +src/renderers/dom/__tests__/ReactDOMProduction-test.js +* should throw with an error code in production + src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js * should set style attribute when styles exist * should warn when using hyphenated style names diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index 0ba4d4a5c07..6ebc9360f2f 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -453,7 +453,6 @@ src/renderers/dom/__tests__/ReactDOMProduction-test.js * should use prod React * should handle a simple flow * should call lifecycle methods -* should throw with an error code in production src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js * should render strings as children @@ -846,18 +845,6 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js * splits deferred work on multiple roots * works on deferred roots in the order they were scheduled * handles interleaved deferred and animation work -* performs animation work in animation callback -* schedules deferred work in animation callback -* schedules deferred work and performs animation work in animation callback -* performs animation work and schedules deferred work in animation callback -* performs deferred work in deferred callback if it has time -* schedules deferred work in deferred callback if it runs out of time -* performs animated work in deferred callback if it has time -* performs animated work and deferred work in deferred callback if it has time -* performs deferred and animated work work in deferred callback if it has time -* schedules animated work in deferred callback if it runs out of time -* schedules animated work and deferred work in deferred callback if it runs out of time -* schedules deferred work and animated work in deferred callback if it runs out of time src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js * can update child nodes of a host instance @@ -878,6 +865,7 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js * calls setState callback even if component bails out * calls componentWillUnmount after a deletion, even if nested * calls componentDidMount/Update after insertion/update +* schedules sync updates when inside componentDidMount/Update * invokes ref callbacks after insertion/update/unmount * supports string refs diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index e3cb91cdeb3..c69395a4185 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -209,7 +209,10 @@ module.exports = function(config : HostConfig) { while (effectfulFiber) { if (effectfulFiber.effectTag & (Update | Callback)) { const current = effectfulFiber.alternate; + const previousPriorityContext = priorityContext; + priorityContext = TaskPriority; commitLifeCycles(current, effectfulFiber); + priorityContext = previousPriorityContext; } const next = effectfulFiber.nextEffect; // Ensure that we clean these up so that we don't accidentally keep them. diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js index 220a69a2fb5..5808ab4c666 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js @@ -449,373 +449,4 @@ describe('ReactIncrementalScheduling', () => { expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]); expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]); }); - - it('performs animation work in animation callback', () => { - class Foo extends React.Component { - componentDidMount() { - // Animation work that will get performed during animation callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - } - render() { - return ; - } - } - - // Schedule animation work - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'a'); - }); - - // Flushing animation work should flush animation work scheduled inside it - ReactNoop.flushAnimationPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('schedules deferred work in animation callback', () => { - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get scheduled during animation callback - ReactNoop.renderToRootWithID(, 'b'); - } - render() { - return ; - } - } - - // Schedule animation work - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'a'); - }); - - // Flushing animation work should not flush the deferred work - ReactNoop.flushAnimationPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - - // Flush the deferred work - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('schedules deferred work and performs animation work in animation callback', () => { - let hasScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get scheduled during animation callback - ReactNoop.renderToRootWithID(, 'b'); - // Animation work that will get performed during animation callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'c'); - }); - // Deferred work that will get scheduled during animation callback - ReactNoop.renderToRootWithID(, 'd'); - hasScheduled = true; - } - render() { - return ; - } - } - - // Schedule animation work - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'a'); - }); - - // Flushing animation work should flush animation work scheduled inside it - ReactNoop.flushAnimationPri(); - expect(hasScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([]); - - // Flush the deferred work - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); - - it('performs animation work and schedules deferred work in animation callback', () => { - let hasScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Animation work that will get performed during animation callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - // Deferred work that will get scheduled during animation callback - ReactNoop.renderToRootWithID(, 'c'); - // Animation work that will get performed during animation callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'd'); - }); - hasScheduled = true; - } - render() { - return ; - } - } - - // Schedule animation work - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'a'); - }); - - // Flushing animation work should flush animation work scheduled inside it - ReactNoop.flushAnimationPri(); - expect(hasScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - - // Flush the deferred work - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); - - it('performs deferred work in deferred callback if it has time', () => { - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'b'); - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should flush deferred work scheduled inside it - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('schedules deferred work in deferred callback if it runs out of time', () => { - let hasScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get scheduled during deferred callback - ReactNoop.renderToRootWithID(, 'b'); - hasScheduled = true; - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flush just enough deferred work to schedule more deferred work - ReactNoop.flushDeferredPri(20); - expect(hasScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - - // Flush the rest of the deferred work - ReactNoop.flushDeferredPri(15); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('performs animated work in deferred callback if it has time', () => { - class Foo extends React.Component { - componentDidMount() { - // Animated work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should flush animated work scheduled inside it - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('performs animated work and deferred work in deferred callback if it has time', () => { - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'b'); - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'c'); - }); - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'd'); - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should flush both deferred and animated work scheduled inside it - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); - - it('performs deferred and animated work work in deferred callback if it has time', () => { - class Foo extends React.Component { - componentDidMount() { - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'c'); - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'd'); - }); - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should flush both deferred and animated work scheduled inside it - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); - - it('schedules animated work in deferred callback if it runs out of time', () => { - let hasScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Animated work that will get scheduled during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - hasScheduled = true; - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flush just enough deferred work to schedule animated work - ReactNoop.flushDeferredPri(20); - expect(hasScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - - // Flush the rest of work in an animated callback. - ReactNoop.flushAnimationPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - }); - - it('schedules animated work and deferred work in deferred callback if it runs out of time', () => { - let isScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'b'); - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'c'); - }); - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'd'); - isScheduled = true; - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should schedule both deferred and animated work - ReactNoop.flushDeferredPri(20); - expect(isScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - expect(ReactNoop.getChildren('c')).toEqual([]); - expect(ReactNoop.getChildren('d')).toEqual([]); - - // Flush the rest of the work - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); - - it('schedules deferred work and animated work in deferred callback if it runs out of time', () => { - let isScheduled = false; - class Foo extends React.Component { - componentDidMount() { - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'b'); - }); - // Deferred work that will get performed during deferred callback - ReactNoop.renderToRootWithID(, 'c'); - // Animation work that will get performed during deferred callback - ReactNoop.performAnimationWork(() => { - ReactNoop.renderToRootWithID(, 'd'); - }); - isScheduled = true; - } - render() { - return ; - } - } - - // Schedule deferred work - ReactNoop.renderToRootWithID(, 'a'); - - // Flushing deferred work should schedule both deferred and animated work - ReactNoop.flushDeferredPri(20); - expect(isScheduled).toBe(true); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([]); - expect(ReactNoop.getChildren('c')).toEqual([]); - expect(ReactNoop.getChildren('d')).toEqual([]); - - // Flush the rest of the work - ReactNoop.flushDeferredPri(); - expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]); - expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]); - expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]); - expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]); - }); }); diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js index 9e64dc43c13..a9294e2501f 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js @@ -1036,6 +1036,66 @@ describe('ReactIncrementalSideEffects', () => { }); + it('schedules sync updates when inside componentDidMount/Update', () => { + var instance; + var ops = []; + + class Foo extends React.Component { + state = { tick: 0 }; + + componentDidMount() { + ops.push('componentDidMount (before setState): ' + this.state.tick); + this.setState({ tick: 1 }); + // We're in a batch. Update hasn't flushed yet. + ops.push('componentDidMount (after setState): ' + this.state.tick); + } + + componentDidUpdate() { + ops.push('componentDidUpdate: ' + this.state.tick); + if (this.state.tick === 2) { + ops.push('componentDidUpdate (before setState): ' + this.state.tick); + this.setState({ tick: 3 }); + ops.push('componentDidUpdate (after setState): ' + this.state.tick); + // We're in a batch. Update hasn't flushed yet. + } + } + + render() { + ops.push('render: ' + this.state.tick); + instance = this; + return ; + } + } + + ReactNoop.render(); + + ReactNoop.flushDeferredPri(20); + expect(ops).toEqual([ + 'render: 0', + 'componentDidMount (before setState): 0', + 'componentDidMount (after setState): 0', + // If the setState inside componentDidMount were deferred, there would be + // no more ops. Because it has Task priority, we get these ops, too: + 'render: 1', + 'componentDidUpdate: 1', + ]); + + ops = []; + instance.setState({ tick: 2 }); + ReactNoop.flushDeferredPri(20); + + expect(ops).toEqual([ + 'render: 2', + 'componentDidUpdate: 2', + 'componentDidUpdate (before setState): 2', + 'componentDidUpdate (after setState): 2', + // If the setState inside componentDidUpdate were deferred, there would be + // no more ops. Because it has Task priority, we get these ops, too: + 'render: 3', + 'componentDidUpdate: 3', + ]); + }); + it('invokes ref callbacks after insertion/update/unmount', () => { var classInstance = null;