Ignore incremental tests that assert on work reuse

it -> xit

The diff looks messier than it actually is because of Prettier.
This commit is contained in:
Andrew Clark
2017-06-30 18:39:04 -07:00
parent 5a32e89b3d
commit de08c2af15
5 changed files with 457 additions and 451 deletions
+4 -22
View File
@@ -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
@@ -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 <div />;
}
function Grandchild() {
ops.push('Grandchild');
return <GreatGrandchild />;
}
class Child extends React.Component {
state = {step: 0};
render() {
child = this;
ops.push('Child');
return <Grandchild />;
}
}
class Sibling extends React.Component {
render() {
ops.push('Sibling');
sibling = this;
function GreatGrandchild() {
ops.push('GreatGrandchild');
return <div />;
}
}
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.
<div key="a"><Child /></div>,
<Sibling key="b" />,
];
}
ReactNoop.render(<Parent />);
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 <div>{props.children}</div>;
}
class Middle extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.children !== nextProps.children;
function Grandchild() {
ops.push('Grandchild');
return <GreatGrandchild />;
}
render() {
ops.push('Middle');
return <span>{this.props.children}</span>;
}
}
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 <Grandchild />;
}
}
render() {
ops.push('Content');
class Sibling extends React.Component {
render() {
ops.push('Sibling');
sibling = this;
return <div />;
}
}
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.
<div key="a"><Child /></div>,
<Sibling key="b" />,
];
}
ReactNoop.render(<Parent />);
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 <div>{props.children}</div>;
}
class Middle extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.children !== nextProps.children;
}
render() {
ops.push('Middle');
return <span>{this.props.children}</span>;
}
}
class Content extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.step !== nextProps.step;
}
render() {
ops.push('Content');
return (
<div>
<Middle>{this.props.step === 0 ? 'Hi' : 'Hello'}</Middle>
<Bar>{this.props.step === 0 ? this.props.text : '-'}</Bar>
<Middle>{this.props.step === 0 ? 'There' : 'World'}</Middle>
</div>
);
}
}
function Foo(props) {
ops.push('Foo');
return (
<div>
<Middle>{this.props.step === 0 ? 'Hi' : 'Hello'}</Middle>
<Bar>{this.props.step === 0 ? this.props.text : '-'}</Bar>
<Middle>{this.props.step === 0 ? 'There' : 'World'}</Middle>
<Bar>{props.text}</Bar>
<div hidden={true}>
<Content step={props.step} text={props.text} />
</div>
</div>
);
}
}
function Foo(props) {
ops.push('Foo');
return (
<div>
<Bar>{props.text}</Bar>
<div hidden={true}>
<Content step={props.step} text={props.text} />
</div>
</div>
);
}
// Init
ReactNoop.render(<Foo text="foo" step={0} />);
ReactNoop.flush();
// Init
ReactNoop.render(<Foo text="foo" step={0} />);
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(<Foo text="bar" step={1} />);
ReactNoop.flushDeferredPri(30 + 5);
// Make a quick update which will schedule low priority work to
// update the middle content.
ReactNoop.render(<Foo text="bar" step={1} />);
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(<Foo text="foo" step={1} />);
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(<Foo text="foo" step={1} />);
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 <span />;
}
}
componentWillMount() {
ops.push('componentWillMount:' + this.state.x);
this.setState({x: this.props.x + '(willMount)'});
function App(props) {
ops.push('App');
return <LifeCycle x={props.x} />;
}
componentDidMount() {
ops.push('componentDidMount:' + this.state.x);
}
render() {
ops.push('render:' + this.state.x);
return <span />;
}
}
function App(props) {
ops.push('App');
return <LifeCycle x={props.x} />;
}
ReactNoop.render(<App x={0} />);
ReactNoop.flushDeferredPri(20);
ReactNoop.render(<App x={0} />);
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(<App x={1} />);
ReactNoop.flush();
ops = [];
ReactNoop.render(<App x={1} />);
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 <span />;
}
}
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 <span />;
}
}
function Sibling() {
ops.push('render sibling');
return <span />;
}
function App(props) {
return [<LifeCycle key="a" x={props.x} />, <Sibling key="b" />];
}
function App(props) {
return [<LifeCycle key="a" x={props.x} />, <Sibling key="b" />];
}
ReactNoop.render(<App x={0} />);
ReactNoop.flush();
ReactNoop.render(<App x={0} />);
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(<App x={0} />);
ReactNoop.flush();
// Update to same props
ReactNoop.render(<App x={0} />);
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(<App x={1} />);
ReactNoop.flushDeferredPri(30);
// Begin updating to new props...
ReactNoop.render(<App x={1} />);
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(<App x={1} />);
ReactNoop.flush();
// ...but we'll interrupt it to rerender the same props.
ReactNoop.render(<App x={1} />);
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 <span>{props.children}</span>;
}
function SecondChild(props) {
return <span>{props.children}</span>;
}
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 <span>{this.props.children}</span>;
}
}
shouldComponentUpdate(nextProps, nextState) {
scuNextProps.push(nextProps);
scuPrevProps.push(this.props);
return this.props.children !== nextProps.children;
}
render() {
renderCounter++;
return <span>{this.props.children}</span>;
}
}
class Middle extends React.Component {
render() {
class Middle extends React.Component {
render() {
return (
<div>
<FirstChild>{this.props.children}</FirstChild>
<SecondChild>{this.props.children}</SecondChild>
</div>
);
}
}
function Root(props) {
return (
<div>
<FirstChild>{this.props.children}</FirstChild>
<SecondChild>{this.props.children}</SecondChild>
<div hidden={true}>
<Middle {...props} />
</div>
);
}
}
function Root(props) {
return (
<div hidden={true}>
<Middle {...props} />
</div>
);
}
// Initial render of the entire tree.
// Renders: Root, Middle, FirstChild, SecondChild
ReactNoop.render(<Root>A</Root>);
ReactNoop.flush();
// Initial render of the entire tree.
// Renders: Root, Middle, FirstChild, SecondChild
ReactNoop.render(<Root>A</Root>);
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(<Root>B</Root>);
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(<Root>B</Root>);
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(<Root>B</Root>);
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(<Root>B</Root>);
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'}]);
},
);
});
@@ -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(
@@ -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 <span prop={this.state.active ? 'X' : this.props.idx} />;
}
}
activate() {
this.setState({active: true});
}
render() {
ops.push('Bar');
return <span prop={this.state.active ? 'X' : this.props.idx} />;
}
}
function Foo(props) {
ops.push('Foo');
return (
<div>
<span prop={props.tick} />
<div hidden={true}>
<Bar idx={props.idx} />
<Bar idx={props.idx} />
<Bar idx={props.idx} />
function Foo(props) {
ops.push('Foo');
return (
<div>
<span prop={props.tick} />
<div hidden={true}>
<Bar idx={props.idx} />
<Bar idx={props.idx} />
<Bar idx={props.idx} />
</div>
</div>
</div>
);
}
ReactNoop.render(<Foo tick={0} idx={0} />);
ReactNoop.flush();
expect(ReactNoop.getChildren()).toEqual([
div(span(0), div(span(0), span(0), span(0))),
]);
);
}
ReactNoop.render(<Foo tick={0} idx={0} />);
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(<Foo tick={1} idx={1} />);
ReactNoop.flushDeferredPri(70 + 5);
expect(ReactNoop.getChildren()).toEqual([
div(
// Updated.
span(1),
ReactNoop.render(<Foo tick={1} idx={1} />);
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?
@@ -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
"
`;