@@ -805,71 +749,62 @@ describe('ReactIncremental', () => {
// Init
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual(['Foo', 'Bar', 'Content', 'Middle', 'Bar', 'Middle']);
-
- ops = [];
+ expect(Scheduler).toFlushAndYield([
+ 'Foo',
+ 'Bar',
+ 'Content',
+ 'Middle',
+ 'Bar',
+ 'Middle',
+ ]);
// 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']);
-
- ops = [];
+ expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
// The middle content is now pending rendering...
ReactNoop.flushDeferredPri(30 + 25 + 5);
- expect(ops).toEqual(['Content', 'Middle', 'Bar']); // One more Middle left.
-
- ops = [];
+ expect(Scheduler).toHaveYielded(['Content', 'Middle', 'Bar']); // One more Middle left.
// 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']);
-
- ops = [];
+ expect(Scheduler).toHaveYielded(['Foo', 'Bar']);
// Since we did nothing to the middle subtree during the interruption,
// we should be able to reuse the reconciliation work that we already did
// without restarting.
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['Middle']);
+ expect(Scheduler).toFlushAndYield(['Middle']);
});
it('memoizes work even if shouldComponentUpdate returns false', () => {
- let ops = [];
class Foo extends React.Component {
shouldComponentUpdate(nextProps) {
// this.props is the memoized props. So this should return true for
// every update except the first one.
const shouldUpdate = this.props.step !== 1;
- ops.push('shouldComponentUpdate: ' + shouldUpdate);
+ Scheduler.unstable_yieldValue('shouldComponentUpdate: ' + shouldUpdate);
return shouldUpdate;
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
return
);
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield(['render']);
- ops = [];
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['shouldComponentUpdate: false']);
+ expect(Scheduler).toFlushAndYield(['shouldComponentUpdate: false']);
- ops = [];
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
// If the memoized props were not updated during last bail out, sCU will
// keep returning false.
'shouldComponentUpdate: true',
@@ -1041,10 +976,8 @@ describe('ReactIncremental', () => {
});
it('can forceUpdate', () => {
- const ops = [];
-
function Baz() {
- ops.push('Baz');
+ Scheduler.unstable_yieldValue('Baz');
return
;
}
@@ -1058,13 +991,13 @@ describe('ReactIncremental', () => {
return false;
}
render() {
- ops.push('Bar');
+ Scheduler.unstable_yieldValue('Bar');
return
;
}
}
function Foo() {
- ops.push('Foo');
+ Scheduler.unstable_yieldValue('Foo');
return (
,
);
ReactNoop.flushDeferredPri(45);
- expect(ops).toEqual(['Foo', 'Bar:A-1', 'Bar:B-1', 'Baz']);
+ expect(Scheduler).toHaveYielded(['Foo', 'Bar:A-1', 'Bar:B-1', 'Baz']);
// Make an update to the same Bar.
instances[0].performAction();
- ops = [];
-
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['Bar:A-1', 'Baz']);
+ expect(Scheduler).toFlushAndYield(['Bar:A-1', 'Baz']);
});
xit('calls componentWillMount twice if the initial render is aborted', () => {
- let ops = [];
-
class LifeCycle extends React.Component {
state = {x: this.props.x};
UNSAFE_componentWillReceiveProps(nextProps) {
- ops.push(
+ Scheduler.unstable_yieldValue(
'componentWillReceiveProps:' + this.state.x + '-' + nextProps.x,
);
this.setState({x: nextProps.x});
}
UNSAFE_componentWillMount() {
- ops.push('componentWillMount:' + this.state.x + '-' + this.props.x);
+ Scheduler.unstable_yieldValue(
+ 'componentWillMount:' + this.state.x + '-' + this.props.x,
+ );
}
componentDidMount() {
- ops.push('componentDidMount:' + this.state.x + '-' + this.props.x);
+ Scheduler.unstable_yieldValue(
+ 'componentDidMount:' + this.state.x + '-' + this.props.x,
+ );
}
render() {
return
;
@@ -1243,12 +1168,12 @@ describe('ReactIncremental', () => {
}
function Trail() {
- ops.push('Trail');
+ Scheduler.unstable_yieldValue('Trail');
return null;
}
function App(props) {
- ops.push('App');
+ Scheduler.unstable_yieldValue('App');
return (
@@ -1260,14 +1185,10 @@ describe('ReactIncremental', () => {
ReactNoop.render(
);
ReactNoop.flushDeferredPri(30);
- expect(ops).toEqual(['App', 'componentWillMount:0-0']);
-
- ops = [];
+ expect(Scheduler).toHaveYielded(['App', 'componentWillMount:0-0']);
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'App',
'componentWillReceiveProps:0-1',
'componentWillMount:1-1',
@@ -1277,45 +1198,40 @@ describe('ReactIncremental', () => {
});
xit('uses state set in componentWillMount even if initial render was aborted', () => {
- let ops = [];
-
class LifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {x: this.props.x + '(ctor)'};
}
UNSAFE_componentWillMount() {
- ops.push('componentWillMount:' + this.state.x);
+ Scheduler.unstable_yieldValue('componentWillMount:' + this.state.x);
this.setState({x: this.props.x + '(willMount)'});
}
componentDidMount() {
- ops.push('componentDidMount:' + this.state.x);
+ Scheduler.unstable_yieldValue('componentDidMount:' + this.state.x);
}
render() {
- ops.push('render:' + this.state.x);
+ Scheduler.unstable_yieldValue('render:' + this.state.x);
return
;
}
}
function App(props) {
- ops.push('App');
+ Scheduler.unstable_yieldValue('App');
return
;
}
ReactNoop.render(
);
ReactNoop.flushDeferredPri(20);
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'App',
'componentWillMount:0(ctor)',
'render:0(willMount)',
]);
- ops = [];
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'App',
'componentWillMount:0(willMount)',
'render:1(willMount)',
@@ -1324,32 +1240,36 @@ describe('ReactIncremental', () => {
});
xit('calls componentWill* twice if an update render is aborted', () => {
- let ops = [];
-
class LifeCycle extends React.Component {
UNSAFE_componentWillMount() {
- ops.push('componentWillMount:' + this.props.x);
+ Scheduler.unstable_yieldValue('componentWillMount:' + this.props.x);
}
componentDidMount() {
- ops.push('componentDidMount:' + this.props.x);
+ Scheduler.unstable_yieldValue('componentDidMount:' + this.props.x);
}
UNSAFE_componentWillReceiveProps(nextProps) {
- ops.push(
+ Scheduler.unstable_yieldValue(
'componentWillReceiveProps:' + this.props.x + '-' + nextProps.x,
);
}
shouldComponentUpdate(nextProps) {
- ops.push('shouldComponentUpdate:' + this.props.x + '-' + nextProps.x);
+ Scheduler.unstable_yieldValue(
+ 'shouldComponentUpdate:' + this.props.x + '-' + nextProps.x,
+ );
return true;
}
UNSAFE_componentWillUpdate(nextProps) {
- ops.push('componentWillUpdate:' + this.props.x + '-' + nextProps.x);
+ Scheduler.unstable_yieldValue(
+ 'componentWillUpdate:' + this.props.x + '-' + nextProps.x,
+ );
}
componentDidUpdate(prevProps) {
- ops.push('componentDidUpdate:' + this.props.x + '-' + prevProps.x);
+ Scheduler.unstable_yieldValue(
+ 'componentDidUpdate:' + this.props.x + '-' + prevProps.x,
+ );
}
render() {
- ops.push('render:' + this.props.x);
+ Scheduler.unstable_yieldValue('render:' + this.props.x);
return
;
}
}
@@ -1357,20 +1277,18 @@ describe('ReactIncremental', () => {
function Sibling() {
// The sibling is used to confirm that we've completed the first child,
// but not yet flushed.
- ops.push('Sibling');
+ Scheduler.unstable_yieldValue('Sibling');
return
;
}
function App(props) {
- ops.push('App');
+ Scheduler.unstable_yieldValue('App');
return [
,
];
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'App',
'componentWillMount:0',
'render:0',
@@ -1378,12 +1296,10 @@ describe('ReactIncremental', () => {
'componentDidMount:0',
]);
- ops = [];
-
ReactNoop.render(
);
ReactNoop.flushDeferredPri(30);
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'App',
'componentWillReceiveProps:0-1',
'shouldComponentUpdate:0-1',
@@ -1393,12 +1309,8 @@ describe('ReactIncremental', () => {
// no componentDidUpdate
]);
- ops = [];
-
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'App',
'componentWillReceiveProps:1-2',
'shouldComponentUpdate:1-2',
@@ -1411,40 +1323,33 @@ describe('ReactIncremental', () => {
});
it('calls getDerivedStateFromProps even for state-only updates', () => {
- let ops = [];
let instance;
class LifeCycle extends React.Component {
state = {};
static getDerivedStateFromProps(props, prevState) {
- ops.push('getDerivedStateFromProps');
+ Scheduler.unstable_yieldValue('getDerivedStateFromProps');
return {foo: 'foo'};
}
changeState() {
this.setState({foo: 'bar'});
}
componentDidUpdate() {
- ops.push('componentDidUpdate');
+ Scheduler.unstable_yieldValue('componentDidUpdate');
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
instance = this;
return null;
}
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual(['getDerivedStateFromProps', 'render']);
+ expect(Scheduler).toFlushAndYield(['getDerivedStateFromProps', 'render']);
expect(instance.state).toEqual({foo: 'foo'});
- ops = [];
-
instance.changeState();
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'getDerivedStateFromProps',
'render',
'componentDidUpdate',
@@ -1486,8 +1391,6 @@ describe('ReactIncremental', () => {
});
xit('does not call componentWillReceiveProps for state-only updates', () => {
- let ops = [];
-
const instances = [];
class LifeCycle extends React.Component {
@@ -1499,26 +1402,32 @@ describe('ReactIncremental', () => {
}
UNSAFE_componentWillMount() {
instances.push(this);
- ops.push('componentWillMount:' + this.state.x);
+ Scheduler.unstable_yieldValue('componentWillMount:' + this.state.x);
}
componentDidMount() {
- ops.push('componentDidMount:' + this.state.x);
+ Scheduler.unstable_yieldValue('componentDidMount:' + this.state.x);
}
UNSAFE_componentWillReceiveProps(nextProps) {
- ops.push('componentWillReceiveProps');
+ Scheduler.unstable_yieldValue('componentWillReceiveProps');
}
shouldComponentUpdate(nextProps, nextState) {
- ops.push('shouldComponentUpdate:' + this.state.x + '-' + nextState.x);
+ Scheduler.unstable_yieldValue(
+ 'shouldComponentUpdate:' + this.state.x + '-' + nextState.x,
+ );
return true;
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
- ops.push('componentWillUpdate:' + this.state.x + '-' + nextState.x);
+ Scheduler.unstable_yieldValue(
+ 'componentWillUpdate:' + this.state.x + '-' + nextState.x,
+ );
}
componentDidUpdate(prevProps, prevState) {
- ops.push('componentDidUpdate:' + this.state.x + '-' + prevState.x);
+ Scheduler.unstable_yieldValue(
+ 'componentDidUpdate:' + this.state.x + '-' + prevState.x,
+ );
}
render() {
- ops.push('render:' + this.state.x);
+ Scheduler.unstable_yieldValue('render:' + this.state.x);
return
;
}
}
@@ -1537,7 +1446,7 @@ describe('ReactIncremental', () => {
});
}
render() {
- ops.push('Wrap');
+ Scheduler.unstable_yieldValue('Wrap');
return
;
}
}
@@ -1545,19 +1454,17 @@ describe('ReactIncremental', () => {
function Sibling() {
// The sibling is used to confirm that we've completed the first child,
// but not yet flushed.
- ops.push('Sibling');
+ Scheduler.unstable_yieldValue('Sibling');
return
;
}
function App(props) {
- ops.push('App');
+ Scheduler.unstable_yieldValue('App');
return [
,
];
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'App',
'Wrap',
'componentWillMount:0',
@@ -1566,14 +1473,12 @@ describe('ReactIncremental', () => {
'componentDidMount:0',
]);
- ops = [];
-
// LifeCycle
instances[1].tick();
ReactNoop.flushDeferredPri(25);
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
// no componentWillReceiveProps
'shouldComponentUpdate:0-1',
'componentWillUpdate:0-1',
@@ -1581,14 +1486,10 @@ describe('ReactIncremental', () => {
// no componentDidUpdate
]);
- ops = [];
-
// LifeCycle
instances[1].tick();
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
// no componentWillReceiveProps
'shouldComponentUpdate:1-2',
'componentWillUpdate:1-2',
@@ -1597,15 +1498,13 @@ describe('ReactIncremental', () => {
'componentDidUpdate:2-0',
]);
- ops = [];
-
// Next we will update props of LifeCycle by updating its parent.
instances[0].tick();
ReactNoop.flushDeferredPri(30);
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'Wrap',
'componentWillReceiveProps',
'shouldComponentUpdate:2-2',
@@ -1614,14 +1513,10 @@ describe('ReactIncremental', () => {
// no componentDidUpdate
]);
- ops = [];
-
// Next we will update LifeCycle directly but not with new props.
instances[1].tick();
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
// This should not trigger another componentWillReceiveProps because
// we never got new props.
'shouldComponentUpdate:2-3',
@@ -1635,37 +1530,35 @@ describe('ReactIncremental', () => {
});
xit('skips will/DidUpdate when bailing unless an update was already in progress', () => {
- let ops = [];
-
class LifeCycle extends React.Component {
UNSAFE_componentWillMount() {
- ops.push('componentWillMount');
+ Scheduler.unstable_yieldValue('componentWillMount');
}
componentDidMount() {
- ops.push('componentDidMount');
+ Scheduler.unstable_yieldValue('componentDidMount');
}
UNSAFE_componentWillReceiveProps(nextProps) {
- ops.push('componentWillReceiveProps');
+ Scheduler.unstable_yieldValue('componentWillReceiveProps');
}
shouldComponentUpdate(nextProps) {
- ops.push('shouldComponentUpdate');
+ Scheduler.unstable_yieldValue('shouldComponentUpdate');
// Bail
return this.props.x !== nextProps.x;
}
UNSAFE_componentWillUpdate(nextProps) {
- ops.push('componentWillUpdate');
+ Scheduler.unstable_yieldValue('componentWillUpdate');
}
componentDidUpdate(prevProps) {
- ops.push('componentDidUpdate');
+ Scheduler.unstable_yieldValue('componentDidUpdate');
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
return
;
}
}
function Sibling() {
- ops.push('render sibling');
+ Scheduler.unstable_yieldValue('render sibling');
return
;
}
@@ -1674,22 +1567,16 @@ describe('ReactIncremental', () => {
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'componentWillMount',
'render',
'render sibling',
'componentDidMount',
]);
- ops = [];
-
// Update to same props
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
-
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'componentWillReceiveProps',
'shouldComponentUpdate',
// no componentWillUpdate
@@ -1698,13 +1585,11 @@ describe('ReactIncremental', () => {
// no componentDidUpdate
]);
- ops = [];
-
// Begin updating to new props...
ReactNoop.render(
);
ReactNoop.flushDeferredPri(30);
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'componentWillReceiveProps',
'shouldComponentUpdate',
'componentWillUpdate',
@@ -1713,14 +1598,12 @@ describe('ReactIncremental', () => {
// no componentDidUpdate yet
]);
- ops = [];
-
// ...but we'll interrupt it to rerender the same props.
ReactNoop.render(
);
expect(Scheduler).toFlushWithoutYielding();
// We can bail out this time, but we must call componentDidUpdate.
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'componentWillReceiveProps',
'shouldComponentUpdate',
// no componentWillUpdate
@@ -1731,7 +1614,6 @@ describe('ReactIncremental', () => {
});
it('can nest batchedUpdates', () => {
- let ops = [];
let instance;
class Foo extends React.Component {
@@ -1744,24 +1626,31 @@ describe('ReactIncremental', () => {
ReactNoop.render(
);
expect(Scheduler).toFlushWithoutYielding();
- ops = [];
ReactNoop.flushSync(() => {
ReactNoop.batchedUpdates(() => {
- instance.setState({n: 1}, () => ops.push('setState 1'));
- instance.setState({n: 2}, () => ops.push('setState 2'));
+ instance.setState({n: 1}, () =>
+ Scheduler.unstable_yieldValue('setState 1'),
+ );
+ instance.setState({n: 2}, () =>
+ Scheduler.unstable_yieldValue('setState 2'),
+ );
ReactNoop.batchedUpdates(() => {
- instance.setState({n: 3}, () => ops.push('setState 3'));
- instance.setState({n: 4}, () => ops.push('setState 4'));
- ops.push('end inner batchedUpdates');
+ instance.setState({n: 3}, () =>
+ Scheduler.unstable_yieldValue('setState 3'),
+ );
+ instance.setState({n: 4}, () =>
+ Scheduler.unstable_yieldValue('setState 4'),
+ );
+ Scheduler.unstable_yieldValue('end inner batchedUpdates');
});
- ops.push('end outer batchedUpdates');
+ Scheduler.unstable_yieldValue('end outer batchedUpdates');
});
});
// ReactNoop.flush() not needed because updates are synchronous
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'end inner batchedUpdates',
'end outer batchedUpdates',
'setState 1',
@@ -1773,7 +1662,6 @@ describe('ReactIncremental', () => {
});
it('can handle if setState callback throws', () => {
- let ops = [];
let instance;
class Foo extends React.Component {
@@ -1786,25 +1674,28 @@ describe('ReactIncremental', () => {
ReactNoop.render(
);
expect(Scheduler).toFlushWithoutYielding();
- ops = [];
function updater({n}) {
return {n: n + 1};
}
- instance.setState(updater, () => ops.push('first callback'));
+ instance.setState(updater, () =>
+ Scheduler.unstable_yieldValue('first callback'),
+ );
instance.setState(updater, () => {
- ops.push('second callback');
+ Scheduler.unstable_yieldValue('second callback');
throw new Error('callback error');
});
- instance.setState(updater, () => ops.push('third callback'));
+ instance.setState(updater, () =>
+ Scheduler.unstable_yieldValue('third callback'),
+ );
expect(() => {
expect(Scheduler).toFlushWithoutYielding();
}).toThrow('callback error');
// The third callback isn't called because the second one throws
- expect(ops).toEqual(['first callback', 'second callback']);
+ expect(Scheduler).toHaveYielded(['first callback', 'second callback']);
expect(instance.state.n).toEqual(3);
});
@@ -1977,7 +1868,6 @@ describe('ReactIncremental', () => {
});
it('does not leak own context into context provider', () => {
- const ops = [];
class Recurse extends React.Component {
static contextTypes = {
n: PropTypes.number,
@@ -1989,7 +1879,9 @@ describe('ReactIncremental', () => {
return {n: (this.context.n || 3) - 1};
}
render() {
- ops.push('Recurse ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'Recurse ' + JSON.stringify(this.context),
+ );
if (this.context.n === 0) {
return null;
}
@@ -1998,30 +1890,30 @@ describe('ReactIncremental', () => {
}
ReactNoop.render(
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'Recurse {}',
+ 'Recurse {"n":2}',
+ 'Recurse {"n":1}',
+ 'Recurse {"n":0}',
+ ]),
+ ).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Recurse',
);
- expect(ops).toEqual([
- 'Recurse {}',
- 'Recurse {"n":2}',
- 'Recurse {"n":1}',
- 'Recurse {"n":0}',
- ]);
});
if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
it('does not leak own context into context provider (factory components)', () => {
- const ops = [];
function Recurse(props, context) {
return {
getChildContext() {
return {n: (context.n || 3) - 1};
},
render() {
- ops.push('Recurse ' + JSON.stringify(context));
+ Scheduler.unstable_yieldValue('Recurse ' + JSON.stringify(context));
if (context.n === 0) {
return null;
}
@@ -2037,7 +1929,14 @@ describe('ReactIncremental', () => {
};
ReactNoop.render(
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'Recurse {}',
+ 'Recurse {"n":2}',
+ 'Recurse {"n":1}',
+ 'Recurse {"n":0}',
+ ]),
+ ).toErrorDev([
'Warning: The
component appears to be a function component that returns a class instance. ' +
'Change Recurse to a class that extends React.Component instead. ' +
"If you can't use a class try assigning the prototype on the function as a workaround. " +
@@ -2048,12 +1947,6 @@ describe('ReactIncremental', () => {
'using it should migrate to the new version.\n\n' +
'Please update the following components: Recurse',
]);
- expect(ops).toEqual([
- 'Recurse {}',
- 'Recurse {"n":2}',
- 'Recurse {"n":1}',
- 'Recurse {"n":0}',
- ]);
});
}
@@ -2118,7 +2011,6 @@ describe('ReactIncremental', () => {
});
it('reads context when setState is below the provider', () => {
- const ops = [];
let statefulInst;
class Intl extends React.Component {
@@ -2129,11 +2021,15 @@ describe('ReactIncremental', () => {
const childContext = {
locale: this.props.locale,
};
- ops.push('Intl:provide ' + JSON.stringify(childContext));
+ Scheduler.unstable_yieldValue(
+ 'Intl:provide ' + JSON.stringify(childContext),
+ );
return childContext;
}
render() {
- ops.push('Intl:read ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'Intl:read ' + JSON.stringify(this.context),
+ );
return this.props.children;
}
}
@@ -2143,13 +2039,17 @@ describe('ReactIncremental', () => {
locale: PropTypes.string,
};
render() {
- ops.push('ShowLocaleClass:read ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'ShowLocaleClass:read ' + JSON.stringify(this.context),
+ );
return this.context.locale;
}
}
function ShowLocaleFn(props, context) {
- ops.push('ShowLocaleFn:read ' + JSON.stringify(context));
+ Scheduler.unstable_yieldValue(
+ 'ShowLocaleFn:read ' + JSON.stringify(context),
+ );
return context.locale;
}
ShowLocaleFn.contextTypes = {
@@ -2165,18 +2065,19 @@ describe('ReactIncremental', () => {
}
function IndirectionFn(props, context) {
- ops.push('IndirectionFn ' + JSON.stringify(context));
+ Scheduler.unstable_yieldValue('IndirectionFn ' + JSON.stringify(context));
return props.children;
}
class IndirectionClass extends React.Component {
render() {
- ops.push('IndirectionClass ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'IndirectionClass ' + JSON.stringify(this.context),
+ );
return this.props.children;
}
}
- ops.length = 0;
ReactNoop.render(
@@ -2189,31 +2090,30 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'Intl:read {}',
+ 'Intl:provide {"locale":"fr"}',
+ 'IndirectionFn {}',
+ 'IndirectionClass {}',
+ 'ShowLocaleClass:read {"locale":"fr"}',
+ 'ShowLocaleFn:read {"locale":"fr"}',
+ ]),
+ ).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Intl, ShowLocaleClass, ShowLocaleFn',
);
- expect(ops).toEqual([
- 'Intl:read {}',
- 'Intl:provide {"locale":"fr"}',
- 'IndirectionFn {}',
- 'IndirectionClass {}',
- 'ShowLocaleClass:read {"locale":"fr"}',
- 'ShowLocaleFn:read {"locale":"fr"}',
- ]);
- ops.length = 0;
statefulInst.setState({x: 1});
expect(Scheduler).toFlushWithoutYielding();
// All work has been memoized because setState()
// happened below the context and could not have affected it.
- expect(ops).toEqual([]);
+ expect(Scheduler).toHaveYielded([]);
});
it('reads context when setState is above the provider', () => {
- const ops = [];
let statefulInst;
class Intl extends React.Component {
@@ -2224,11 +2124,15 @@ describe('ReactIncremental', () => {
const childContext = {
locale: this.props.locale,
};
- ops.push('Intl:provide ' + JSON.stringify(childContext));
+ Scheduler.unstable_yieldValue(
+ 'Intl:provide ' + JSON.stringify(childContext),
+ );
return childContext;
}
render() {
- ops.push('Intl:read ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'Intl:read ' + JSON.stringify(this.context),
+ );
return this.props.children;
}
}
@@ -2238,13 +2142,17 @@ describe('ReactIncremental', () => {
locale: PropTypes.string,
};
render() {
- ops.push('ShowLocaleClass:read ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'ShowLocaleClass:read ' + JSON.stringify(this.context),
+ );
return this.context.locale;
}
}
function ShowLocaleFn(props, context) {
- ops.push('ShowLocaleFn:read ' + JSON.stringify(context));
+ Scheduler.unstable_yieldValue(
+ 'ShowLocaleFn:read ' + JSON.stringify(context),
+ );
return context.locale;
}
ShowLocaleFn.contextTypes = {
@@ -2252,13 +2160,15 @@ describe('ReactIncremental', () => {
};
function IndirectionFn(props, context) {
- ops.push('IndirectionFn ' + JSON.stringify(context));
+ Scheduler.unstable_yieldValue('IndirectionFn ' + JSON.stringify(context));
return props.children;
}
class IndirectionClass extends React.Component {
render() {
- ops.push('IndirectionClass ' + JSON.stringify(this.context));
+ Scheduler.unstable_yieldValue(
+ 'IndirectionClass ' + JSON.stringify(this.context),
+ );
return this.props.children;
}
}
@@ -2271,7 +2181,6 @@ describe('ReactIncremental', () => {
}
}
- ops.length = 0;
ReactNoop.render(
@@ -2282,25 +2191,24 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'Intl:read {}',
+ 'Intl:provide {"locale":"fr"}',
+ 'IndirectionFn {}',
+ 'IndirectionClass {}',
+ 'ShowLocaleClass:read {"locale":"fr"}',
+ 'ShowLocaleFn:read {"locale":"fr"}',
+ ]),
+ ).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Intl, ShowLocaleClass, ShowLocaleFn',
);
- expect(ops).toEqual([
- 'Intl:read {}',
- 'Intl:provide {"locale":"fr"}',
- 'IndirectionFn {}',
- 'IndirectionClass {}',
- 'ShowLocaleClass:read {"locale":"fr"}',
- 'ShowLocaleFn:read {"locale":"fr"}',
- ]);
- ops.length = 0;
statefulInst.setState({locale: 'gr'});
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
// Intl is below setState() so it might have been
// affected by it. Therefore we re-render and recompute
// its child context.
@@ -2421,38 +2329,47 @@ describe('ReactIncremental', () => {
});
it('should not recreate masked context unless inputs have changed', () => {
- const ops = [];
-
let scuCounter = 0;
class MyComponent extends React.Component {
static contextTypes = {};
componentDidMount(prevProps, prevState) {
- ops.push('componentDidMount');
+ Scheduler.unstable_yieldValue('componentDidMount');
this.setState({setStateInCDU: true});
}
componentDidUpdate(prevProps, prevState) {
- ops.push('componentDidUpdate');
+ Scheduler.unstable_yieldValue('componentDidUpdate');
if (this.state.setStateInCDU) {
this.setState({setStateInCDU: false});
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
- ops.push('componentWillReceiveProps');
+ Scheduler.unstable_yieldValue('componentWillReceiveProps');
this.setState({setStateInCDU: true});
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
return null;
}
shouldComponentUpdate(nextProps, nextState) {
- ops.push('shouldComponentUpdate');
+ Scheduler.unstable_yieldValue('shouldComponentUpdate');
return scuCounter++ < 5; // Don't let test hang
}
}
ReactNoop.render(
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'render',
+ 'componentDidMount',
+ 'shouldComponentUpdate',
+ 'render',
+ 'componentDidUpdate',
+ 'shouldComponentUpdate',
+ 'render',
+ 'componentDidUpdate',
+ ]),
+ ).toErrorDev(
[
'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
'Legacy context API has been detected within a strict-mode tree.\n\n' +
@@ -2462,17 +2379,6 @@ describe('ReactIncremental', () => {
],
{withoutStack: 1},
);
-
- expect(ops).toEqual([
- 'render',
- 'componentDidMount',
- 'shouldComponentUpdate',
- 'render',
- 'componentDidUpdate',
- 'shouldComponentUpdate',
- 'render',
- 'componentDidUpdate',
- ]);
});
xit('should reuse memoized work if pointers are updated before calling lifecycles', () => {
@@ -2559,7 +2465,6 @@ describe('ReactIncremental', () => {
});
it('updates descendants with new context values', () => {
- const rendered = [];
let instance;
class TopContextProvider extends React.Component {
@@ -2590,7 +2495,7 @@ describe('ReactIncremental', () => {
count: PropTypes.number,
};
render = () => {
- rendered.push(`count:${this.context.count}`);
+ Scheduler.unstable_yieldValue(`count:${this.context.count}`);
return null;
};
}
@@ -2603,20 +2508,17 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Child, TopContextProvider',
);
- expect(rendered).toEqual(['count:0']);
instance.updateCount();
- expect(Scheduler).toFlushWithoutYielding();
- expect(rendered).toEqual(['count:0', 'count:1']);
+ expect(Scheduler).toFlushAndYield(['count:1']);
});
it('updates descendants with multiple context-providing ancestors with new context values', () => {
- const rendered = [];
let instance;
class TopContextProvider extends React.Component {
@@ -2653,7 +2555,7 @@ describe('ReactIncremental', () => {
count: PropTypes.number,
};
render = () => {
- rendered.push(`count:${this.context.count}`);
+ Scheduler.unstable_yieldValue(`count:${this.context.count}`);
return null;
};
}
@@ -2666,20 +2568,17 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Child, MiddleContextProvider, TopContextProvider',
);
- expect(rendered).toEqual(['count:0']);
instance.updateCount();
- expect(Scheduler).toFlushWithoutYielding();
- expect(rendered).toEqual(['count:0', 'count:1']);
+ expect(Scheduler).toFlushAndYield(['count:1']);
});
it('should not update descendants with new context values if shouldComponentUpdate returns false', () => {
- const rendered = [];
let instance;
class TopContextProvider extends React.Component {
@@ -2723,7 +2622,7 @@ describe('ReactIncremental', () => {
count: PropTypes.number,
};
render = () => {
- rendered.push(`count:${this.context.count}`);
+ Scheduler.unstable_yieldValue(`count:${this.context.count}`);
return null;
};
}
@@ -2738,20 +2637,17 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() => expect(Scheduler).toFlushAndYield(['count:0'])).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Child, MiddleContextProvider, TopContextProvider',
);
- expect(rendered).toEqual(['count:0']);
instance.updateCount();
expect(Scheduler).toFlushWithoutYielding();
- expect(rendered).toEqual(['count:0']);
});
it('should update descendants with new context values if setState() is called in the middle of the tree', () => {
- const rendered = [];
let middleInstance;
let topInstance;
@@ -2805,7 +2701,9 @@ describe('ReactIncremental', () => {
name: PropTypes.string,
};
render = () => {
- rendered.push(`count:${this.context.count}, name:${this.context.name}`);
+ Scheduler.unstable_yieldValue(
+ `count:${this.context.count}, name:${this.context.name}`,
+ );
return null;
};
}
@@ -2820,22 +2718,18 @@ describe('ReactIncremental', () => {
,
);
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield(['count:0, name:brian']),
+ ).toErrorDev(
'Legacy context API has been detected within a strict-mode tree.\n\n' +
'The old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.\n\n' +
'Please update the following components: Child, MiddleContextProvider, TopContextProvider',
);
- expect(rendered).toEqual(['count:0, name:brian']);
topInstance.updateCount();
expect(Scheduler).toFlushWithoutYielding();
- expect(rendered).toEqual(['count:0, name:brian']);
middleInstance.updateName('not brian');
- expect(Scheduler).toFlushWithoutYielding();
- expect(rendered).toEqual([
- 'count:0, name:brian',
- 'count:1, name:not brian',
- ]);
+ expect(Scheduler).toFlushAndYield(['count:1, name:not brian']);
});
it('does not interrupt for update at same priority', () => {
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
index 8eebd6e3a6..4ba335869e 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
@@ -20,7 +20,6 @@ describe('ReactIncrementalErrorHandling', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('shared/ReactFeatureFlags');
- ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
PropTypes = require('prop-types');
React = require('react');
@@ -554,27 +553,26 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('catches render error in a boundary during synchronous mounting', () => {
- const ops = [];
class ErrorBoundary extends React.Component {
state = {error: null};
componentDidCatch(error) {
- ops.push('ErrorBoundary componentDidCatch');
+ Scheduler.unstable_yieldValue('ErrorBoundary componentDidCatch');
this.setState({error});
}
render() {
if (this.state.error) {
- ops.push('ErrorBoundary render error');
+ Scheduler.unstable_yieldValue('ErrorBoundary render error');
return (
);
}
- ops.push('ErrorBoundary render success');
+ Scheduler.unstable_yieldValue('ErrorBoundary render success');
return this.props.children;
}
}
function BrokenRender(props) {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
@@ -586,7 +584,7 @@ describe('ReactIncrementalErrorHandling', () => {
);
});
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'ErrorBoundary render success',
'BrokenRender',
@@ -602,27 +600,26 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('catches render error in a boundary during batched mounting', () => {
- const ops = [];
class ErrorBoundary extends React.Component {
state = {error: null};
componentDidCatch(error) {
- ops.push('ErrorBoundary componentDidCatch');
+ Scheduler.unstable_yieldValue('ErrorBoundary componentDidCatch');
this.setState({error});
}
render() {
if (this.state.error) {
- ops.push('ErrorBoundary render error');
+ Scheduler.unstable_yieldValue('ErrorBoundary render error');
return (
);
}
- ops.push('ErrorBoundary render success');
+ Scheduler.unstable_yieldValue('ErrorBoundary render success');
return this.props.children;
}
}
function BrokenRender(props) {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
@@ -635,7 +632,7 @@ describe('ReactIncrementalErrorHandling', () => {
);
});
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'ErrorBoundary render success',
'BrokenRender',
@@ -651,20 +648,19 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('propagates an error from a noop error boundary during full deferred mounting', () => {
- const ops = [];
class RethrowErrorBoundary extends React.Component {
componentDidCatch(error) {
- ops.push('RethrowErrorBoundary componentDidCatch');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary componentDidCatch');
throw error;
}
render() {
- ops.push('RethrowErrorBoundary render');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary render');
return this.props.children;
}
}
function BrokenRender() {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
@@ -675,19 +671,18 @@ describe('ReactIncrementalErrorHandling', () => {
);
expect(() => {
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield([
+ 'RethrowErrorBoundary render',
+ 'BrokenRender',
+
+ // React retries one more time
+ 'RethrowErrorBoundary render',
+ 'BrokenRender',
+
+ // Errored again on retry. Now handle it.
+ 'RethrowErrorBoundary componentDidCatch',
+ ]);
}).toThrow('Hello');
- expect(ops).toEqual([
- 'RethrowErrorBoundary render',
- 'BrokenRender',
-
- // React retries one more time
- 'RethrowErrorBoundary render',
- 'BrokenRender',
-
- // Errored again on retry. Now handle it.
- 'RethrowErrorBoundary componentDidCatch',
- ]);
expect(ReactNoop.getChildren()).toEqual([]);
});
@@ -733,20 +728,19 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('propagates an error from a noop error boundary during synchronous mounting', () => {
- const ops = [];
class RethrowErrorBoundary extends React.Component {
componentDidCatch(error) {
- ops.push('RethrowErrorBoundary componentDidCatch');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary componentDidCatch');
throw error;
}
render() {
- ops.push('RethrowErrorBoundary render');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary render');
return this.props.children;
}
}
function BrokenRender() {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
@@ -759,7 +753,7 @@ describe('ReactIncrementalErrorHandling', () => {
);
});
}).toThrow('Hello');
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'RethrowErrorBoundary render',
'BrokenRender',
@@ -774,20 +768,19 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('propagates an error from a noop error boundary during batched mounting', () => {
- const ops = [];
class RethrowErrorBoundary extends React.Component {
componentDidCatch(error) {
- ops.push('RethrowErrorBoundary componentDidCatch');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary componentDidCatch');
throw error;
}
render() {
- ops.push('RethrowErrorBoundary render');
+ Scheduler.unstable_yieldValue('RethrowErrorBoundary render');
return this.props.children;
}
}
function BrokenRender() {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
@@ -803,7 +796,7 @@ describe('ReactIncrementalErrorHandling', () => {
);
});
}).toThrow('Hello');
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'RethrowErrorBoundary render',
'BrokenRender',
@@ -864,15 +857,13 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('can schedule updates after uncaught error in render on mount', () => {
- let ops = [];
-
function BrokenRender() {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
throw new Error('Hello');
}
function Foo() {
- ops.push('Foo');
+ Scheduler.unstable_yieldValue('Foo');
return null;
}
@@ -880,23 +871,19 @@ describe('ReactIncrementalErrorHandling', () => {
expect(() => {
expect(Scheduler).toFlushWithoutYielding();
}).toThrow('Hello');
- expect(ops).toEqual([
+ ReactNoop.render(
);
+ expect(Scheduler).toHaveYielded([
'BrokenRender',
// React retries one more time
'BrokenRender',
// Errored again on retry
]);
- ops = [];
- ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['Foo']);
+ expect(Scheduler).toFlushAndYield(['Foo']);
});
it('can schedule updates after uncaught error in render on update', () => {
- let ops = [];
-
function BrokenRender(props) {
- ops.push('BrokenRender');
+ Scheduler.unstable_yieldValue('BrokenRender');
if (props.throw) {
throw new Error('Hello');
}
@@ -904,34 +891,29 @@ describe('ReactIncrementalErrorHandling', () => {
}
function Foo() {
- ops.push('Foo');
+ Scheduler.unstable_yieldValue('Foo');
return null;
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- ops = [];
+ expect(Scheduler).toFlushAndYield(['BrokenRender']);
expect(() => {
ReactNoop.render(
);
expect(Scheduler).toFlushWithoutYielding();
}).toThrow('Hello');
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'BrokenRender',
// React retries one more time
'BrokenRender',
// Errored again on retry
]);
- ops = [];
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['Foo']);
+ expect(Scheduler).toFlushAndYield(['Foo']);
});
it('can schedule updates after uncaught error during umounting', () => {
- let ops = [];
-
class BrokenComponentWillUnmount extends React.Component {
render() {
return
;
@@ -942,7 +924,7 @@ describe('ReactIncrementalErrorHandling', () => {
}
function Foo() {
- ops.push('Foo');
+ Scheduler.unstable_yieldValue('Foo');
return null;
}
@@ -954,10 +936,8 @@ describe('ReactIncrementalErrorHandling', () => {
expect(Scheduler).toFlushWithoutYielding();
}).toThrow('Hello');
- ops = [];
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['Foo']);
+ expect(Scheduler).toFlushAndYield(['Foo']);
});
it('should not attempt to recover an unmounting error boundary', () => {
@@ -1342,13 +1322,14 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('unmounts components with uncaught errors', () => {
- const ops = [];
let inst;
class BrokenRenderAndUnmount extends React.Component {
state = {fail: false};
componentWillUnmount() {
- ops.push('BrokenRenderAndUnmount componentWillUnmount');
+ Scheduler.unstable_yieldValue(
+ 'BrokenRenderAndUnmount componentWillUnmount',
+ );
}
render() {
inst = this;
@@ -1361,7 +1342,7 @@ describe('ReactIncrementalErrorHandling', () => {
class Parent extends React.Component {
componentWillUnmount() {
- ops.push('Parent componentWillUnmount [!]');
+ Scheduler.unstable_yieldValue('Parent componentWillUnmount [!]');
throw new Error('One does not simply unmount me.');
}
render() {
@@ -1383,7 +1364,7 @@ describe('ReactIncrementalErrorHandling', () => {
expect(Scheduler).toFlushWithoutYielding();
}).toThrowError('Hello.');
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
// Attempt to clean up.
// Errors in parents shouldn't stop children from unmounting.
'Parent componentWillUnmount [!]',
@@ -1394,11 +1375,9 @@ describe('ReactIncrementalErrorHandling', () => {
});
it('does not interrupt unmounting if detaching a ref throws', () => {
- let ops = [];
-
class Bar extends React.Component {
componentWillUnmount() {
- ops.push('Bar unmount');
+ Scheduler.unstable_yieldValue('Bar unmount');
}
render() {
return
;
@@ -1407,10 +1386,10 @@ describe('ReactIncrementalErrorHandling', () => {
function barRef(inst) {
if (inst === null) {
- ops.push('barRef detach');
+ Scheduler.unstable_yieldValue('barRef detach');
throw new Error('Detach error');
}
- ops.push('barRef attach');
+ Scheduler.unstable_yieldValue('barRef attach');
}
function Foo(props) {
@@ -1418,16 +1397,13 @@ describe('ReactIncrementalErrorHandling', () => {
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual(['barRef attach']);
+ expect(Scheduler).toFlushAndYield(['barRef attach']);
expect(ReactNoop.getChildren()).toEqual([div(span('Bar'))]);
- ops = [];
-
// Unmount
ReactNoop.render(
);
expect(Scheduler).toFlushAndThrow('Detach error');
- expect(ops).toEqual([
+ expect(Scheduler).toHaveYielded([
'barRef detach',
// Bar should unmount even though its ref threw an error while detaching
'Bar unmount',
@@ -1451,37 +1427,36 @@ describe('ReactIncrementalErrorHandling', () => {
it('error boundaries capture non-errors', () => {
spyOnProd(console, 'error');
spyOnDev(console, 'error');
- const ops = [];
class ErrorBoundary extends React.Component {
state = {error: null};
componentDidCatch(error) {
// Should not be called
- ops.push('componentDidCatch');
+ Scheduler.unstable_yieldValue('componentDidCatch');
this.setState({error});
}
render() {
if (this.state.error) {
- ops.push('ErrorBoundary (catch)');
+ Scheduler.unstable_yieldValue('ErrorBoundary (catch)');
return (
);
}
- ops.push('ErrorBoundary (try)');
+ Scheduler.unstable_yieldValue('ErrorBoundary (try)');
return this.props.children;
}
}
function Indirection(props) {
- ops.push('Indirection');
+ Scheduler.unstable_yieldValue('Indirection');
return props.children;
}
const notAnError = {nonStandardMessage: 'oops'};
function BadRender() {
- ops.push('BadRender');
+ Scheduler.unstable_yieldValue('BadRender');
throw notAnError;
}
@@ -1492,9 +1467,8 @@ describe('ReactIncrementalErrorHandling', () => {
,
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'ErrorBoundary (try)',
'Indirection',
'BadRender',
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js
similarity index 100%
rename from packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.js
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js
similarity index 96%
rename from packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js
index f2b4d956d0..0fda09f04f 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalScheduling-test.js
@@ -11,15 +11,13 @@
'use strict';
let React;
-let ReactFeatureFlags;
let ReactNoop;
let Scheduler;
describe('ReactIncrementalScheduling', () => {
beforeEach(() => {
jest.resetModules();
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
- ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
+
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
@@ -324,11 +322,10 @@ describe('ReactIncrementalScheduling', () => {
it('nested updates are always deferred, even inside unbatchedUpdates', () => {
let instance;
- const ops = [];
class Foo extends React.Component {
state = {step: 0};
componentDidUpdate() {
- ops.push('componentDidUpdate: ' + this.state.step);
+ Scheduler.unstable_yieldValue('componentDidUpdate: ' + this.state.step);
if (this.state.step === 1) {
ReactNoop.unbatchedUpdates(() => {
// This is a nested state update, so it should not be
@@ -336,30 +333,27 @@ describe('ReactIncrementalScheduling', () => {
// in unbatchedUpdates.
this.setState({step: 2});
});
+ expect(Scheduler).toHaveYielded([
+ 'render: 1',
+ 'componentDidUpdate: 1',
+ ]);
expect(ReactNoop).toMatchRenderedOutput(
);
}
}
render() {
- ops.push('render: ' + this.state.step);
+ Scheduler.unstable_yieldValue('render: ' + this.state.step);
instance = this;
return
;
}
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield(['render: 0']);
expect(ReactNoop).toMatchRenderedOutput(
);
ReactNoop.flushSync(() => {
instance.setState({step: 1});
});
+ expect(Scheduler).toHaveYielded(['render: 2', 'componentDidUpdate: 2']);
expect(ReactNoop).toMatchRenderedOutput(
);
-
- expect(ops).toEqual([
- 'render: 0',
- 'render: 1',
- 'componentDidUpdate: 1',
- 'render: 2',
- 'componentDidUpdate: 2',
- ]);
});
});
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
similarity index 100%
rename from packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.js
similarity index 98%
rename from packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.js
index cd80343153..fdf7d37e82 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalTriangle-test.js
@@ -11,15 +11,13 @@
'use strict';
let React;
-let ReactFeatureFlags;
let ReactNoop;
let Scheduler;
describe('ReactIncrementalTriangle', () => {
beforeEach(() => {
jest.resetModules();
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
- ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
+
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
@@ -184,12 +182,6 @@ describe('ReactIncrementalTriangle', () => {
class Triangle extends React.Component {
constructor(props) {
super();
- this.index = triangles.length;
- triangles.push(this);
- if (props.remainingDepth === 0) {
- this.leafIndex = leafTriangles.length;
- leafTriangles.push(this);
- }
this.state = {isActive: false};
this.child = React.createRef(null);
}
@@ -206,6 +198,14 @@ describe('ReactIncrementalTriangle', () => {
this.state.isActive !== nextState.isActive
);
}
+ componentDidMount() {
+ this.index = triangles.length;
+ triangles.push(this);
+ if (this.props.remainingDepth === 0) {
+ this.leafIndex = leafTriangles.length;
+ leafTriangles.push(this);
+ }
+ }
componentDidUpdate() {
if (this.child.current !== null) {
const {prop: currentCounter} = JSON.parse(this.child.current.prop);
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
similarity index 94%
rename from packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
index 74c206a927..51753f0e3f 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
@@ -11,15 +11,13 @@
'use strict';
let React;
-let ReactFeatureFlags;
let ReactNoop;
let Scheduler;
describe('ReactIncrementalUpdates', () => {
beforeEach(() => {
jest.resetModuleRegistry();
- ReactFeatureFlags = require('shared/ReactFeatureFlags');
- ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
+
React = require('react');
ReactNoop = require('react-noop-renderer');
Scheduler = require('scheduler');
@@ -79,24 +77,23 @@ describe('ReactIncrementalUpdates', () => {
it('only drops updates with equal or lesser priority when replaceState is called', () => {
let instance;
- let ops = [];
class Foo extends React.Component {
state = {};
componentDidMount() {
- ops.push('componentDidMount');
+ Scheduler.unstable_yieldValue('componentDidMount');
}
componentDidUpdate() {
- ops.push('componentDidUpdate');
+ Scheduler.unstable_yieldValue('componentDidUpdate');
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
instance = this;
return
;
}
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield(['render', 'componentDidMount']);
ReactNoop.flushSync(() => {
ReactNoop.deferredUpdates(() => {
@@ -114,19 +111,11 @@ describe('ReactIncrementalUpdates', () => {
// Even though a replaceState has been already scheduled, it hasn't been
// flushed yet because it has async priority.
expect(instance.state).toEqual({a: 'a', b: 'b'});
- expect(ops).toEqual([
- 'render',
- 'componentDidMount',
- 'render',
- 'componentDidUpdate',
- ]);
+ expect(Scheduler).toHaveYielded(['render', 'componentDidUpdate']);
- ops = [];
-
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield(['render', 'componentDidUpdate']);
// Now the rest of the updates are flushed, including the replaceState.
expect(instance.state).toEqual({c: 'c', d: 'd'});
- expect(ops).toEqual(['render', 'componentDidUpdate']);
});
it('can abort an update, schedule additional updates, and resume', () => {
@@ -298,27 +287,25 @@ describe('ReactIncrementalUpdates', () => {
});
it('does not call callbacks that are scheduled by another callback until a later commit', () => {
- const ops = [];
class Foo extends React.Component {
state = {};
componentDidMount() {
- ops.push('did mount');
+ Scheduler.unstable_yieldValue('did mount');
this.setState({a: 'a'}, () => {
- ops.push('callback a');
+ Scheduler.unstable_yieldValue('callback a');
this.setState({b: 'b'}, () => {
- ops.push('callback b');
+ Scheduler.unstable_yieldValue('callback b');
});
});
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
return
;
}
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
- expect(ops).toEqual([
+ expect(Scheduler).toFlushAndYield([
'render',
'did mount',
'render',
@@ -330,30 +317,27 @@ describe('ReactIncrementalUpdates', () => {
it('gives setState during reconciliation the same priority as whatever level is currently reconciling', () => {
let instance;
- let ops = [];
class Foo extends React.Component {
state = {};
UNSAFE_componentWillReceiveProps() {
- ops.push('componentWillReceiveProps');
+ Scheduler.unstable_yieldValue('componentWillReceiveProps');
this.setState({b: 'b'});
}
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
instance = this;
return
;
}
}
ReactNoop.render(
);
expect(() =>
- expect(Scheduler).toFlushWithoutYielding(),
+ expect(Scheduler).toFlushAndYield(['render']),
).toErrorDev(
'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
{withoutStack: true},
);
- ops = [];
-
ReactNoop.flushSync(() => {
instance.setState({a: 'a'});
@@ -361,44 +345,45 @@ describe('ReactIncrementalUpdates', () => {
});
expect(instance.state).toEqual({a: 'a', b: 'b'});
- expect(ops).toEqual(['componentWillReceiveProps', 'render']);
+ expect(Scheduler).toHaveYielded(['componentWillReceiveProps', 'render']);
});
it('enqueues setState inside an updater function as if the in-progress update is progressed (and warns)', () => {
let instance;
- const ops = [];
class Foo extends React.Component {
state = {};
render() {
- ops.push('render');
+ Scheduler.unstable_yieldValue('render');
instance = this;
return
;
}
}
ReactNoop.render(
);
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield([
+ // Initial render
+ 'render',
+ ]);
instance.setState(function a() {
- ops.push('setState updater');
+ Scheduler.unstable_yieldValue('setState updater');
this.setState({b: 'b'});
return {a: 'a'};
});
- expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
+ expect(() =>
+ expect(Scheduler).toFlushAndYield([
+ 'setState updater',
+ // Update b is enqueued with the same priority as update a, so it should
+ // be flushed in the same commit.
+ 'render',
+ ]),
+ ).toErrorDev(
'An update (setState, replaceState, or forceUpdate) was scheduled ' +
'from inside an update function. Update functions should be pure, ' +
'with zero side-effects. Consider using componentDidUpdate or a ' +
'callback.',
);
- expect(ops).toEqual([
- // Initial render
- 'render',
- 'setState updater',
- // Update b is enqueued with the same priority as update a, so it should
- // be flushed in the same commit.
- 'render',
- ]);
expect(instance.state).toEqual({a: 'a', b: 'b'});
// Test deduplication (no additional warnings expected)
@@ -406,7 +391,7 @@ describe('ReactIncrementalUpdates', () => {
this.setState({a: 'a'});
return {b: 'b'};
});
- expect(Scheduler).toFlushWithoutYielding();
+ expect(Scheduler).toFlushAndYield(['render']);
});
it('getDerivedStateFromProps should update base state of updateQueue (based on product bug)', () => {
diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.internal.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js
similarity index 100%
rename from packages/react-reconciler/src/__tests__/ReactMemo-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactMemo-test.js
diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js
similarity index 99%
rename from packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactNewContext-test.js
index 58609e6fc4..346d4e8d52 100644
--- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js
@@ -1219,7 +1219,7 @@ describe('ReactNewContext', () => {
spyOnDev(console, 'error');
const Context = React.createContext(0);
ReactNoop.render(
);
- expect(Scheduler).toFlushAndThrow('render is not a function');
+ expect(Scheduler).toFlushAndThrow('is not a function');
if (__DEV__) {
expect(console.error.calls.argsFor(0)[0]).toContain(
'A context consumer was rendered with multiple children, or a child ' +
diff --git a/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js b/packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
similarity index 100%
rename from packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.internal.js
rename to packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
diff --git a/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.internal.js b/packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
similarity index 100%
rename from packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.internal.js
rename to packages/react-test-renderer/src/__tests__/ReactTestRendererAsync-test.js
diff --git a/packages/use-subscription/src/__tests__/useSubscription-test.internal.js b/packages/use-subscription/src/__tests__/useSubscription-test.js
similarity index 100%
rename from packages/use-subscription/src/__tests__/useSubscription-test.internal.js
rename to packages/use-subscription/src/__tests__/useSubscription-test.js