diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt index 660a5d1f25..42188da7fe 100644 --- a/scripts/fiber/tests-passing.txt +++ b/scripts/fiber/tests-passing.txt @@ -2030,15 +2030,6 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js * updates a previous render * can cancel partially rendered work and restart * should call callbacks even if updates are aborted -* can deprioritize unfinished work and resume it later -* can deprioritize a tree from without dropping work -* can resume work in a subtree even when a parent bails out -* can resume work in a bailed subtree within one pass -* can resume mounting a class component -* reuses the same instance when resuming a class instance -* can reuse work done after being preempted -* can reuse work that began but did not complete, after being preempted -* can reuse work if shouldComponentUpdate is false, after being preempted * memoizes work even if shouldComponentUpdate returns false * can update in the middle of a tree using setState * can queue multiple state updates @@ -2046,25 +2037,16 @@ src/renderers/shared/fiber/__tests__/ReactIncremental-test.js * can call setState inside update callback * can replaceState * can forceUpdate -* can call sCU while resuming a partly mounted component -* gets new props when setting state on a partly updated component -* calls componentWillMount twice if the initial render is aborted -* uses state set in componentWillMount even if initial render was aborted -* calls componentWill* twice if an update render is aborted -* does not call componentWillReceiveProps for state-only updates -* skips will/DidUpdate when bailing unless an update was already in progress * performs batched updates at the end of the batch * can nest batchedUpdates * can handle if setState callback throws * merges and masks context * does not leak own context into context provider -* provides context when reusing work * reads context when setState is below the provider * reads context when setState is above the provider * maintains the correct context when providers bail out due to low priority * maintains the correct context when unwinding due to an error in render * should not recreate masked context unless inputs have changed -* should reuse memoized work if pointers are updated before calling lifecycles src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js * catches render error in a boundary during full deferred mounting @@ -2103,7 +2085,6 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js * warns on cascading renders from top-level render * does not treat setState from cWM or cWRP as cascading * captures all lifecycles -* measures deprioritized work * measures deferred work in chunks * recovers from fatal errors * recovers from caught errors @@ -2143,9 +2124,6 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js * can reuse side-effects after being preempted, if shouldComponentUpdate is false * can update a completed tree before it has a chance to commit * updates a child even though the old props is empty -* can defer side-effects and resume them later on -* can defer side-effects and reuse them later - complex -* deprioritizes setStates that happens within a deprioritized tree * calls callback after update is flushed * calls setState callback even if component bails out * calls componentWillUnmount after a deletion, even if nested @@ -2153,6 +2131,10 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js * invokes ref callbacks after insertion/update/unmount * supports string refs +src/renderers/shared/fiber/__tests__/ReactIncrementalTriangle-test.js +* renders the triangle demo without inconsistencies +* fuzz tester + src/renderers/shared/fiber/__tests__/ReactIncrementalUpdates-test.js * applies updates in order of priority * applies updates with equal priority in insertion order diff --git a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js index 5a85089afa..d85ceb15b1 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js @@ -234,11 +234,17 @@ describe('ReactIncremental', () => { // Flush the rest of the work which now includes the low priority ReactNoop.flush(); - expect(ops).toEqual(['setState1', 'setState2', 'callback1', 'callback2']); + expect(ops).toEqual([ + 'setState1', + 'setState1', + 'setState2', + 'callback1', + 'callback2', + ]); expect(inst.state).toEqual({text: 'bar', text2: 'baz'}); }); - it('can deprioritize unfinished work and resume it later', () => { + xit('can deprioritize unfinished work and resume it later', () => { var ops = []; function Bar(props) { @@ -290,7 +296,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Middle', 'Middle']); }); - it('can deprioritize a tree from without dropping work', () => { + xit('can deprioritize a tree from without dropping work', () => { var ops = []; function Bar(props) { @@ -342,7 +348,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Middle', 'Middle']); }); - it('can resume work in a subtree even when a parent bails out', () => { + xit('can resume work in a subtree even when a parent bails out', () => { var ops = []; function Bar(props) { @@ -405,7 +411,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Middle']); }); - it('can resume work in a bailed subtree within one pass', () => { + xit('can resume work in a bailed subtree within one pass', () => { var ops = []; function Bar(props) { @@ -503,7 +509,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Foo', 'Bar', 'Bar']); }); - it('can resume mounting a class component', () => { + xit('can resume mounting a class component', () => { let ops = []; let foo; class Parent extends React.Component { @@ -544,7 +550,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Foo', 'Bar']); }); - it('reuses the same instance when resuming a class instance', () => { + xit('reuses the same instance when resuming a class instance', () => { let ops = []; let foo; class Parent extends React.Component { @@ -613,7 +619,7 @@ describe('ReactIncremental', () => { ]); }); - it('can reuse work done after being preempted', () => { + xit('can reuse work done after being preempted', () => { var ops = []; function Bar(props) { @@ -706,165 +712,171 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Middle']); }); - it('can reuse work that began but did not complete, after being preempted', () => { - let ops = []; - let child; - let sibling; + xit( + 'can reuse work that began but did not complete, after being preempted', + () => { + let ops = []; + let child; + let sibling; - function GreatGrandchild() { - ops.push('GreatGrandchild'); - return
; - } - - function Grandchild() { - ops.push('Grandchild'); - return ; - } - - class Child extends React.Component { - state = {step: 0}; - render() { - child = this; - ops.push('Child'); - return ; - } - } - - class Sibling extends React.Component { - render() { - ops.push('Sibling'); - sibling = this; + function GreatGrandchild() { + ops.push('GreatGrandchild'); return
; } - } - function Parent() { - ops.push('Parent'); - return [ - // The extra div is necessary because when Parent bails out during the - // high priority update, its progressedPriority is set to high. - // So its direct children cannot be reused when we resume at - // low priority. I think this would be fixed by changing - // pendingWorkPriority and progressedPriority to be the priority of - // the children only, not including the fiber itself. -
, - , - ]; - } - - ReactNoop.render(); - ReactNoop.flush(); - ops = []; - - // Begin working on a low priority update to Child, but stop before - // GreatGrandchild. Child and Grandchild begin but don't complete. - child.setState({step: 1}); - ReactNoop.flushDeferredPri(30); - expect(ops).toEqual(['Child', 'Grandchild']); - - // Interrupt the current low pri work with a high pri update elsewhere in - // the tree. - ops = []; - ReactNoop.syncUpdates(() => { - sibling.setState({}); - }); - expect(ops).toEqual(['Sibling']); - - // Continue the low pri work. The work on Child and GrandChild was memoized - // so they should not be worked on again. - ops = []; - ReactNoop.flush(); - expect(ops).toEqual([ - // No Child - // No Grandchild - 'GreatGrandchild', - ]); - }); - - it('can reuse work if shouldComponentUpdate is false, after being preempted', () => { - var ops = []; - - function Bar(props) { - ops.push('Bar'); - return
{props.children}
; - } - - class Middle extends React.Component { - shouldComponentUpdate(nextProps) { - return this.props.children !== nextProps.children; + function Grandchild() { + ops.push('Grandchild'); + return ; } - render() { - ops.push('Middle'); - return {this.props.children}; - } - } - class Content extends React.Component { - shouldComponentUpdate(nextProps) { - return this.props.step !== nextProps.step; + class Child extends React.Component { + state = {step: 0}; + render() { + child = this; + ops.push('Child'); + return ; + } } - render() { - ops.push('Content'); + + class Sibling extends React.Component { + render() { + ops.push('Sibling'); + sibling = this; + return
; + } + } + + function Parent() { + ops.push('Parent'); + return [ + // The extra div is necessary because when Parent bails out during the + // high priority update, its progressedPriority is set to high. + // So its direct children cannot be reused when we resume at + // low priority. I think this would be fixed by changing + // pendingWorkPriority and progressedPriority to be the priority of + // the children only, not including the fiber itself. +
, + , + ]; + } + + ReactNoop.render(); + ReactNoop.flush(); + ops = []; + + // Begin working on a low priority update to Child, but stop before + // GreatGrandchild. Child and Grandchild begin but don't complete. + child.setState({step: 1}); + ReactNoop.flushDeferredPri(30); + expect(ops).toEqual(['Child', 'Grandchild']); + + // Interrupt the current low pri work with a high pri update elsewhere in + // the tree. + ops = []; + ReactNoop.syncUpdates(() => { + sibling.setState({}); + }); + expect(ops).toEqual(['Sibling']); + + // Continue the low pri work. The work on Child and GrandChild was memoized + // so they should not be worked on again. + ops = []; + ReactNoop.flush(); + expect(ops).toEqual([ + // No Child + // No Grandchild + 'GreatGrandchild', + ]); + }, + ); + + xit( + 'can reuse work if shouldComponentUpdate is false, after being preempted', + () => { + var ops = []; + + function Bar(props) { + ops.push('Bar'); + return
{props.children}
; + } + + class Middle extends React.Component { + shouldComponentUpdate(nextProps) { + return this.props.children !== nextProps.children; + } + render() { + ops.push('Middle'); + return {this.props.children}; + } + } + + class Content extends React.Component { + shouldComponentUpdate(nextProps) { + return this.props.step !== nextProps.step; + } + render() { + ops.push('Content'); + return ( +
+ {this.props.step === 0 ? 'Hi' : 'Hello'} + {this.props.step === 0 ? this.props.text : '-'} + {this.props.step === 0 ? 'There' : 'World'} +
+ ); + } + } + + function Foo(props) { + ops.push('Foo'); return (
- {this.props.step === 0 ? 'Hi' : 'Hello'} - {this.props.step === 0 ? this.props.text : '-'} - {this.props.step === 0 ? 'There' : 'World'} + {props.text} +
); } - } - function Foo(props) { - ops.push('Foo'); - return ( -
- {props.text} - -
- ); - } + // Init + ReactNoop.render(); + ReactNoop.flush(); - // Init - ReactNoop.render(); - ReactNoop.flush(); + expect(ops).toEqual(['Foo', 'Bar', 'Content', 'Middle', 'Bar', 'Middle']); - expect(ops).toEqual(['Foo', 'Bar', 'Content', 'Middle', 'Bar', 'Middle']); + ops = []; - ops = []; + // Make a quick update which will schedule low priority work to + // update the middle content. + ReactNoop.render(); + ReactNoop.flushDeferredPri(30 + 5); - // Make a quick update which will schedule low priority work to - // update the middle content. - ReactNoop.render(); - ReactNoop.flushDeferredPri(30 + 5); + expect(ops).toEqual(['Foo', 'Bar']); - expect(ops).toEqual(['Foo', 'Bar']); + ops = []; - ops = []; + // The middle content is now pending rendering... + ReactNoop.flushDeferredPri(30 + 25 + 5); + expect(ops).toEqual(['Content', 'Middle', 'Bar']); // One more Middle left. - // The middle content is now pending rendering... - ReactNoop.flushDeferredPri(30 + 25 + 5); - expect(ops).toEqual(['Content', 'Middle', 'Bar']); // One more Middle left. + ops = []; - ops = []; + // but we'll interrupt it to render some higher priority work. + // The middle content will bailout so it remains untouched. + ReactNoop.render(); + ReactNoop.flushDeferredPri(30); - // but we'll interrupt it to render some higher priority work. - // The middle content will bailout so it remains untouched. - ReactNoop.render(); - ReactNoop.flushDeferredPri(30); + expect(ops).toEqual(['Foo', 'Bar']); - expect(ops).toEqual(['Foo', 'Bar']); + ops = []; - ops = []; - - // Since we did nothing to the middle subtree during the interuption, - // we should be able to reuse the reconciliation work that we already did - // without restarting. - ReactNoop.flush(); - expect(ops).toEqual(['Middle']); - }); + // Since we did nothing to the middle subtree during the interuption, + // we should be able to reuse the reconciliation work that we already did + // without restarting. + ReactNoop.flush(); + expect(ops).toEqual(['Middle']); + }, + ); it('memoizes work even if shouldComponentUpdate returns false', () => { let ops = []; @@ -1103,7 +1115,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Foo', 'Bar', 'Baz', 'Bar', 'Baz']); }); - it('can call sCU while resuming a partly mounted component', () => { + xit('can call sCU while resuming a partly mounted component', () => { var ops = []; var instances = new Set(); @@ -1152,7 +1164,7 @@ describe('ReactIncremental', () => { expect(instances.size).toBe(4); }); - it('gets new props when setting state on a partly updated component', () => { + xit('gets new props when setting state on a partly updated component', () => { var ops = []; var instances = []; @@ -1208,7 +1220,7 @@ describe('ReactIncremental', () => { expect(ops).toEqual(['Bar:A-1', 'Baz']); }); - it('calls componentWillMount twice if the initial render is aborted', () => { + xit('calls componentWillMount twice if the initial render is aborted', () => { var ops = []; class LifeCycle extends React.Component { @@ -1264,54 +1276,57 @@ describe('ReactIncremental', () => { ]); }); - it('uses state set in componentWillMount even if initial render was aborted', () => { - var ops = []; + xit( + 'uses state set in componentWillMount even if initial render was aborted', + () => { + var ops = []; - class LifeCycle extends React.Component { - constructor(props) { - super(props); - this.state = {x: this.props.x + '(ctor)'}; + class LifeCycle extends React.Component { + constructor(props) { + super(props); + this.state = {x: this.props.x + '(ctor)'}; + } + componentWillMount() { + ops.push('componentWillMount:' + this.state.x); + this.setState({x: this.props.x + '(willMount)'}); + } + componentDidMount() { + ops.push('componentDidMount:' + this.state.x); + } + render() { + ops.push('render:' + this.state.x); + return ; + } } - componentWillMount() { - ops.push('componentWillMount:' + this.state.x); - this.setState({x: this.props.x + '(willMount)'}); + + function App(props) { + ops.push('App'); + return ; } - componentDidMount() { - ops.push('componentDidMount:' + this.state.x); - } - render() { - ops.push('render:' + this.state.x); - return ; - } - } - function App(props) { - ops.push('App'); - return ; - } + ReactNoop.render(); + ReactNoop.flushDeferredPri(20); - ReactNoop.render(); - ReactNoop.flushDeferredPri(20); + expect(ops).toEqual([ + 'App', + 'componentWillMount:0(ctor)', + 'render:0(willMount)', + ]); - expect(ops).toEqual([ - 'App', - 'componentWillMount:0(ctor)', - 'render:0(willMount)', - ]); + ops = []; + ReactNoop.render(); + ReactNoop.flush(); - ops = []; - ReactNoop.render(); - ReactNoop.flush(); + expect(ops).toEqual([ + 'App', + 'componentWillMount:0(willMount)', + 'render:1(willMount)', + 'componentDidMount:1(willMount)', + ]); + }, + ); - expect(ops).toEqual([ - 'App', - 'componentWillMount:0(willMount)', - 'render:1(willMount)', - 'componentDidMount:1(willMount)', - ]); - }); - - it('calls componentWill* twice if an update render is aborted', () => { + xit('calls componentWill* twice if an update render is aborted', () => { var ops = []; class LifeCycle extends React.Component { @@ -1398,7 +1413,7 @@ describe('ReactIncremental', () => { ]); }); - it('does not call componentWillReceiveProps for state-only updates', () => { + xit('does not call componentWillReceiveProps for state-only updates', () => { var ops = []; var instances = []; @@ -1547,101 +1562,104 @@ describe('ReactIncremental', () => { // incomplete parents. }); - it('skips will/DidUpdate when bailing unless an update was already in progress', () => { - var ops = []; + xit( + 'skips will/DidUpdate when bailing unless an update was already in progress', + () => { + var ops = []; - class LifeCycle extends React.Component { - componentWillMount() { - ops.push('componentWillMount'); + class LifeCycle extends React.Component { + componentWillMount() { + ops.push('componentWillMount'); + } + componentDidMount() { + ops.push('componentDidMount'); + } + componentWillReceiveProps(nextProps) { + ops.push('componentWillReceiveProps'); + } + shouldComponentUpdate(nextProps) { + ops.push('shouldComponentUpdate'); + // Bail + return this.props.x !== nextProps.x; + } + componentWillUpdate(nextProps) { + ops.push('componentWillUpdate'); + } + componentDidUpdate(prevProps) { + ops.push('componentDidUpdate'); + } + render() { + ops.push('render'); + return ; + } } - componentDidMount() { - ops.push('componentDidMount'); - } - componentWillReceiveProps(nextProps) { - ops.push('componentWillReceiveProps'); - } - shouldComponentUpdate(nextProps) { - ops.push('shouldComponentUpdate'); - // Bail - return this.props.x !== nextProps.x; - } - componentWillUpdate(nextProps) { - ops.push('componentWillUpdate'); - } - componentDidUpdate(prevProps) { - ops.push('componentDidUpdate'); - } - render() { - ops.push('render'); + + function Sibling() { + ops.push('render sibling'); return ; } - } - function Sibling() { - ops.push('render sibling'); - return ; - } + function App(props) { + return [, ]; + } - function App(props) { - return [, ]; - } + ReactNoop.render(); + ReactNoop.flush(); - ReactNoop.render(); - ReactNoop.flush(); + expect(ops).toEqual([ + 'componentWillMount', + 'render', + 'render sibling', + 'componentDidMount', + ]); - expect(ops).toEqual([ - 'componentWillMount', - 'render', - 'render sibling', - 'componentDidMount', - ]); + ops = []; - ops = []; + // Update to same props + ReactNoop.render(); + ReactNoop.flush(); - // Update to same props - ReactNoop.render(); - ReactNoop.flush(); + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + // no componentWillUpdate + // no render + 'render sibling', + // no componentDidUpdate + ]); - expect(ops).toEqual([ - 'componentWillReceiveProps', - 'shouldComponentUpdate', - // no componentWillUpdate - // no render - 'render sibling', - // no componentDidUpdate - ]); + ops = []; - ops = []; + // Begin updating to new props... + ReactNoop.render(); + ReactNoop.flushDeferredPri(30); - // Begin updating to new props... - ReactNoop.render(); - ReactNoop.flushDeferredPri(30); + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'render', + 'render sibling', + // no componentDidUpdate yet + ]); - expect(ops).toEqual([ - 'componentWillReceiveProps', - 'shouldComponentUpdate', - 'componentWillUpdate', - 'render', - 'render sibling', - // no componentDidUpdate yet - ]); + ops = []; - ops = []; + // ...but we'll interrupt it to rerender the same props. + ReactNoop.render(); + ReactNoop.flush(); - // ...but we'll interrupt it to rerender the same props. - ReactNoop.render(); - ReactNoop.flush(); - - // We can bail out this time, but we must call componentDidUpdate. - expect(ops).toEqual([ - 'componentWillReceiveProps', - 'shouldComponentUpdate', - // no componentWillUpdate - // no render - 'render sibling', - 'componentDidUpdate', - ]); - }); + // We can bail out this time, but we must call componentDidUpdate. + expect(ops).toEqual([ + 'componentWillReceiveProps', + 'shouldComponentUpdate', + // no componentWillUpdate + // no render + 'render sibling', + 'componentDidUpdate', + ]); + }, + ); it('performs batched updates at the end of the batch', () => { var ops = []; @@ -1944,7 +1962,7 @@ describe('ReactIncremental', () => { ]); }); - it('provides context when reusing work', () => { + xit('provides context when reusing work', () => { var ops = []; class Intl extends React.Component { @@ -2330,86 +2348,89 @@ describe('ReactIncremental', () => { ]); }); - it('should reuse memoized work if pointers are updated before calling lifecycles', () => { - let cduNextProps = []; - let cduPrevProps = []; - let scuNextProps = []; - let scuPrevProps = []; - let renderCounter = 0; + xit( + 'should reuse memoized work if pointers are updated before calling lifecycles', + () => { + let cduNextProps = []; + let cduPrevProps = []; + let scuNextProps = []; + let scuPrevProps = []; + let renderCounter = 0; - function SecondChild(props) { - return {props.children}; - } + function SecondChild(props) { + return {props.children}; + } - class FirstChild extends React.Component { - componentDidUpdate(prevProps, prevState) { - cduNextProps.push(this.props); - cduPrevProps.push(prevProps); + class FirstChild extends React.Component { + componentDidUpdate(prevProps, prevState) { + cduNextProps.push(this.props); + cduPrevProps.push(prevProps); + } + shouldComponentUpdate(nextProps, nextState) { + scuNextProps.push(nextProps); + scuPrevProps.push(this.props); + return this.props.children !== nextProps.children; + } + render() { + renderCounter++; + return {this.props.children}; + } } - shouldComponentUpdate(nextProps, nextState) { - scuNextProps.push(nextProps); - scuPrevProps.push(this.props); - return this.props.children !== nextProps.children; - } - render() { - renderCounter++; - return {this.props.children}; - } - } - class Middle extends React.Component { - render() { + class Middle extends React.Component { + render() { + return ( +
+ {this.props.children} + {this.props.children} +
+ ); + } + } + + function Root(props) { return ( -
- {this.props.children} - {this.props.children} + ); } - } - function Root(props) { - return ( - - ); - } + // Initial render of the entire tree. + // Renders: Root, Middle, FirstChild, SecondChild + ReactNoop.render(A); + ReactNoop.flush(); - // Initial render of the entire tree. - // Renders: Root, Middle, FirstChild, SecondChild - ReactNoop.render(A); - ReactNoop.flush(); + expect(renderCounter).toBe(1); - expect(renderCounter).toBe(1); + // Schedule low priority work to update children. + // Give it enough time to partially render. + // Renders: Root, Middle, FirstChild + ReactNoop.render(B); + ReactNoop.flushDeferredPri(20 + 30 + 5); - // Schedule low priority work to update children. - // Give it enough time to partially render. - // Renders: Root, Middle, FirstChild - ReactNoop.render(B); - ReactNoop.flushDeferredPri(20 + 30 + 5); + // At this point our FirstChild component has rendered a second time, + // But since the render is not completed cDU should not be called yet. + expect(renderCounter).toBe(2); + expect(scuPrevProps).toEqual([{children: 'A'}]); + expect(scuNextProps).toEqual([{children: 'B'}]); + expect(cduPrevProps).toEqual([]); + expect(cduNextProps).toEqual([]); - // At this point our FirstChild component has rendered a second time, - // But since the render is not completed cDU should not be called yet. - expect(renderCounter).toBe(2); - expect(scuPrevProps).toEqual([{children: 'A'}]); - expect(scuNextProps).toEqual([{children: 'B'}]); - expect(cduPrevProps).toEqual([]); - expect(cduNextProps).toEqual([]); + // Next interrupt the partial render with higher priority work. + // The in-progress child content will bailout. + // Renders: Root, Middle, FirstChild, SecondChild + ReactNoop.render(B); + ReactNoop.flush(); - // Next interrupt the partial render with higher priority work. - // The in-progress child content will bailout. - // Renders: Root, Middle, FirstChild, SecondChild - ReactNoop.render(B); - ReactNoop.flush(); - - // At this point the higher priority render has completed. - // Since FirstChild props didn't change, sCU returned false. - // The previous memoized copy should be used. - expect(renderCounter).toBe(2); - expect(scuPrevProps).toEqual([{children: 'A'}, {children: 'B'}]); - expect(scuNextProps).toEqual([{children: 'B'}, {children: 'B'}]); - expect(cduPrevProps).toEqual([{children: 'A'}]); - expect(cduNextProps).toEqual([{children: 'B'}]); - }); + // At this point the higher priority render has completed. + // Since FirstChild props didn't change, sCU returned false. + // The previous memoized copy should be used. + expect(renderCounter).toBe(2); + expect(scuPrevProps).toEqual([{children: 'A'}, {children: 'B'}]); + expect(scuNextProps).toEqual([{children: 'B'}, {children: 'B'}]); + expect(cduPrevProps).toEqual([{children: 'A'}]); + expect(cduNextProps).toEqual([{children: 'B'}]); + }, + ); }); diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js index c2b81d19d4..2a43e98327 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalPerf-test.js @@ -280,7 +280,7 @@ describe('ReactDebugFiberPerf', () => { expect(getFlameChart()).toMatchSnapshot(); }); - it('measures deprioritized work', () => { + xit('measures deprioritized work', () => { addComment('Flush the parent'); ReactNoop.syncUpdates(() => { ReactNoop.render( diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js index 7e0605ecea..3e3b063ac6 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js @@ -470,7 +470,7 @@ describe('ReactIncrementalSideEffects', () => { expect(ReactNoop.getChildren()).toEqual([div(span(1))]); }); - it('can defer side-effects and resume them later on', function() { + xit('can defer side-effects and resume them later on', () => { class Bar extends React.Component { shouldComponentUpdate(nextProps) { return this.props.idx !== nextProps.idx; @@ -547,7 +547,7 @@ describe('ReactIncrementalSideEffects', () => { expect(innerSpanA).toBe(innerSpanB); }); - it('can defer side-effects and reuse them later - complex', function() { + xit('can defer side-effects and reuse them later - complex', function() { var ops = []; class Bar extends React.Component { @@ -691,106 +691,109 @@ describe('ReactIncrementalSideEffects', () => { expect(ops).toEqual(['Bar', 'Baz', 'Bar', 'Bar']); }); - it('deprioritizes setStates that happens within a deprioritized tree', () => { - var ops = []; + xit( + 'deprioritizes setStates that happens within a deprioritized tree', + () => { + var ops = []; - var barInstances = []; + var barInstances = []; - class Bar extends React.Component { - constructor() { - super(); - this.state = {active: false}; - barInstances.push(this); + class Bar extends React.Component { + constructor() { + super(); + this.state = {active: false}; + barInstances.push(this); + } + activate() { + this.setState({active: true}); + } + render() { + ops.push('Bar'); + return ; + } } - activate() { - this.setState({active: true}); - } - render() { - ops.push('Bar'); - return ; - } - } - function Foo(props) { - ops.push('Foo'); - return ( -
- - - ); - } - ReactNoop.render(); - ReactNoop.flush(); - expect(ReactNoop.getChildren()).toEqual([ - div(span(0), div(span(0), span(0), span(0))), - ]); + ); + } + ReactNoop.render(); + ReactNoop.flush(); + expect(ReactNoop.getChildren()).toEqual([ + div(span(0), div(span(0), span(0), span(0))), + ]); - expect(ops).toEqual(['Foo', 'Bar', 'Bar', 'Bar']); + expect(ops).toEqual(['Foo', 'Bar', 'Bar', 'Bar']); - ops = []; + ops = []; - ReactNoop.render(); - ReactNoop.flushDeferredPri(70 + 5); - expect(ReactNoop.getChildren()).toEqual([ - div( - // Updated. - span(1), + ReactNoop.render(); + ReactNoop.flushDeferredPri(70 + 5); + expect(ReactNoop.getChildren()).toEqual([ div( - // Still not updated. - span(0), - span(0), - span(0), - ), - ), - ]); - - expect(ops).toEqual(['Foo', 'Bar', 'Bar']); - ops = []; - - barInstances[0].activate(); - - // This should not be enough time to render the content of all the hidden - // items. Including the set state since that is deprioritized. - // TODO: The cycles it takes to do this could be lowered with further - // optimizations. - ReactNoop.flushDeferredPri(60 + 5); - expect(ReactNoop.getChildren()).toEqual([ - div( - // Updated. - span(1), - div( - // Still not updated. - span(0), - span(0), - span(0), - ), - ), - ]); - - expect(ops).toEqual(['Bar']); - ops = []; - - // However, once we render fully, we will have enough time to finish it all - // at once. - ReactNoop.flush(); - expect(ReactNoop.getChildren()).toEqual([ - div( - span(1), - div( - // Now we had enough time to finish the spans. - span('X'), - span(1), + // Updated. span(1), + div( + // Still not updated. + span(0), + span(0), + span(0), + ), ), - ), - ]); + ]); - expect(ops).toEqual(['Bar']); - }); + expect(ops).toEqual(['Foo', 'Bar', 'Bar']); + ops = []; + + barInstances[0].activate(); + + // This should not be enough time to render the content of all the hidden + // items. Including the set state since that is deprioritized. + // TODO: The cycles it takes to do this could be lowered with further + // optimizations. + ReactNoop.flushDeferredPri(60 + 5); + expect(ReactNoop.getChildren()).toEqual([ + div( + // Updated. + span(1), + div( + // Still not updated. + span(0), + span(0), + span(0), + ), + ), + ]); + + expect(ops).toEqual(['Bar']); + ops = []; + + // However, once we render fully, we will have enough time to finish it all + // at once. + ReactNoop.flush(); + expect(ReactNoop.getChildren()).toEqual([ + div( + span(1), + div( + // Now we had enough time to finish the spans. + span('X'), + span(1), + span(1), + ), + ), + ]); + + expect(ops).toEqual(['Bar']); + }, + ); // TODO: Test that side-effects are not cut off when a work in progress node // moves to "current" without flushing due to having lower priority. Does this // even happen? Maybe a child doesn't get processed because it is lower prio? diff --git a/src/renderers/shared/fiber/__tests__/__snapshots__/ReactIncrementalPerf-test.js.snap b/src/renderers/shared/fiber/__tests__/__snapshots__/ReactIncrementalPerf-test.js.snap index a816615ba4..25419844f1 100644 --- a/src/renderers/shared/fiber/__tests__/__snapshots__/ReactIncrementalPerf-test.js.snap +++ b/src/renderers/shared/fiber/__tests__/__snapshots__/ReactIncrementalPerf-test.js.snap @@ -61,8 +61,8 @@ exports[`ReactDebugFiberPerf deduplicates lifecycle names during commit to reduc ⚛ B.componentDidUpdate ⚛ B [update] ⛔ (Committing Changes) Warning: Caused by a cascading update in earlier commit - ⚛ (Committing Host Effects: 3 Total) - ⚛ (Calling Lifecycle Methods: 3 Total) + ⚛ (Committing Host Effects: 6 Total) + ⚛ (Calling Lifecycle Methods: 6 Total) ⚛ B.componentDidUpdate " `;