mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Merge pull request #8206 from acdlite/fibertasklifecycleupdates
[Fiber] Updates from inside componentDidMount/Update should have Task priority
This commit is contained in:
@@ -72,6 +72,9 @@ src/renderers/art/__tests__/ReactART-test.js
|
||||
* resolves refs before componentDidMount
|
||||
* resolves refs before componentDidUpdate
|
||||
|
||||
src/renderers/dom/__tests__/ReactDOMProduction-test.js
|
||||
* should throw with an error code in production
|
||||
|
||||
src/renderers/dom/shared/__tests__/CSSPropertyOperations-test.js
|
||||
* should set style attribute when styles exist
|
||||
* should warn when using hyphenated style names
|
||||
|
||||
@@ -453,7 +453,6 @@ src/renderers/dom/__tests__/ReactDOMProduction-test.js
|
||||
* should use prod React
|
||||
* should handle a simple flow
|
||||
* should call lifecycle methods
|
||||
* should throw with an error code in production
|
||||
|
||||
src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
|
||||
* should render strings as children
|
||||
@@ -843,18 +842,6 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalScheduling-test.js
|
||||
* splits deferred work on multiple roots
|
||||
* works on deferred roots in the order they were scheduled
|
||||
* handles interleaved deferred and animation work
|
||||
* performs animation work in animation callback
|
||||
* schedules deferred work in animation callback
|
||||
* schedules deferred work and performs animation work in animation callback
|
||||
* performs animation work and schedules deferred work in animation callback
|
||||
* performs deferred work in deferred callback if it has time
|
||||
* schedules deferred work in deferred callback if it runs out of time
|
||||
* performs animated work in deferred callback if it has time
|
||||
* performs animated work and deferred work in deferred callback if it has time
|
||||
* performs deferred and animated work work in deferred callback if it has time
|
||||
* schedules animated work in deferred callback if it runs out of time
|
||||
* schedules animated work and deferred work in deferred callback if it runs out of time
|
||||
* schedules deferred work and animated work in deferred callback if it runs out of time
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
|
||||
* can update child nodes of a host instance
|
||||
@@ -875,6 +862,7 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
|
||||
* calls setState callback even if component bails out
|
||||
* calls componentWillUnmount after a deletion, even if nested
|
||||
* calls componentDidMount/Update after insertion/update
|
||||
* schedules sync updates when inside componentDidMount/Update
|
||||
* invokes ref callbacks after insertion/update/unmount
|
||||
* supports string refs
|
||||
|
||||
|
||||
@@ -209,7 +209,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
|
||||
while (effectfulFiber) {
|
||||
if (effectfulFiber.effectTag & (Update | Callback)) {
|
||||
const current = effectfulFiber.alternate;
|
||||
const previousPriorityContext = priorityContext;
|
||||
priorityContext = TaskPriority;
|
||||
commitLifeCycles(current, effectfulFiber);
|
||||
priorityContext = previousPriorityContext;
|
||||
}
|
||||
const next = effectfulFiber.nextEffect;
|
||||
// Ensure that we clean these up so that we don't accidentally keep them.
|
||||
|
||||
@@ -449,373 +449,4 @@ describe('ReactIncrementalScheduling', () => {
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:3')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:3')]);
|
||||
});
|
||||
|
||||
it('performs animation work in animation callback', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animation work that will get performed during animation callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule animation work
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
});
|
||||
|
||||
// Flushing animation work should flush animation work scheduled inside it
|
||||
ReactNoop.flushAnimationPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('schedules deferred work in animation callback', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get scheduled during animation callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule animation work
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
});
|
||||
|
||||
// Flushing animation work should not flush the deferred work
|
||||
ReactNoop.flushAnimationPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
|
||||
// Flush the deferred work
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('schedules deferred work and performs animation work in animation callback', () => {
|
||||
let hasScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get scheduled during animation callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
// Animation work that will get performed during animation callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
});
|
||||
// Deferred work that will get scheduled during animation callback
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
hasScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule animation work
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
});
|
||||
|
||||
// Flushing animation work should flush animation work scheduled inside it
|
||||
ReactNoop.flushAnimationPri();
|
||||
expect(hasScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([]);
|
||||
|
||||
// Flush the deferred work
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
|
||||
it('performs animation work and schedules deferred work in animation callback', () => {
|
||||
let hasScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animation work that will get performed during animation callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
// Deferred work that will get scheduled during animation callback
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
// Animation work that will get performed during animation callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
});
|
||||
hasScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule animation work
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
});
|
||||
|
||||
// Flushing animation work should flush animation work scheduled inside it
|
||||
ReactNoop.flushAnimationPri();
|
||||
expect(hasScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
|
||||
// Flush the deferred work
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
|
||||
it('performs deferred work in deferred callback if it has time', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should flush deferred work scheduled inside it
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('schedules deferred work in deferred callback if it runs out of time', () => {
|
||||
let hasScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get scheduled during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
hasScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flush just enough deferred work to schedule more deferred work
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
expect(hasScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
|
||||
// Flush the rest of the deferred work
|
||||
ReactNoop.flushDeferredPri(15);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('performs animated work in deferred callback if it has time', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animated work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should flush animated work scheduled inside it
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('performs animated work and deferred work in deferred callback if it has time', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
});
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should flush both deferred and animated work scheduled inside it
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
|
||||
it('performs deferred and animated work work in deferred callback if it has time', () => {
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should flush both deferred and animated work scheduled inside it
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
|
||||
it('schedules animated work in deferred callback if it runs out of time', () => {
|
||||
let hasScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animated work that will get scheduled during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
hasScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flush just enough deferred work to schedule animated work
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
expect(hasScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
|
||||
// Flush the rest of work in an animated callback.
|
||||
ReactNoop.flushAnimationPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
});
|
||||
|
||||
it('schedules animated work and deferred work in deferred callback if it runs out of time', () => {
|
||||
let isScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
});
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
isScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should schedule both deferred and animated work
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
expect(isScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([]);
|
||||
|
||||
// Flush the rest of the work
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
|
||||
it('schedules deferred work and animated work in deferred callback if it runs out of time', () => {
|
||||
let isScheduled = false;
|
||||
class Foo extends React.Component {
|
||||
componentDidMount() {
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="b:1" />, 'b');
|
||||
});
|
||||
// Deferred work that will get performed during deferred callback
|
||||
ReactNoop.renderToRootWithID(<span prop="c:1" />, 'c');
|
||||
// Animation work that will get performed during deferred callback
|
||||
ReactNoop.performAnimationWork(() => {
|
||||
ReactNoop.renderToRootWithID(<span prop="d:1" />, 'd');
|
||||
});
|
||||
isScheduled = true;
|
||||
}
|
||||
render() {
|
||||
return <span prop="a:1" />;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule deferred work
|
||||
ReactNoop.renderToRootWithID(<Foo />, 'a');
|
||||
|
||||
// Flushing deferred work should schedule both deferred and animated work
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
expect(isScheduled).toBe(true);
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([]);
|
||||
|
||||
// Flush the rest of the work
|
||||
ReactNoop.flushDeferredPri();
|
||||
expect(ReactNoop.getChildren('a')).toEqual([span('a:1')]);
|
||||
expect(ReactNoop.getChildren('b')).toEqual([span('b:1')]);
|
||||
expect(ReactNoop.getChildren('c')).toEqual([span('c:1')]);
|
||||
expect(ReactNoop.getChildren('d')).toEqual([span('d:1')]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1036,6 +1036,66 @@ describe('ReactIncrementalSideEffects', () => {
|
||||
|
||||
});
|
||||
|
||||
it('schedules sync updates when inside componentDidMount/Update', () => {
|
||||
var instance;
|
||||
var ops = [];
|
||||
|
||||
class Foo extends React.Component {
|
||||
state = { tick: 0 };
|
||||
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount (before setState): ' + this.state.tick);
|
||||
this.setState({ tick: 1 });
|
||||
// We're in a batch. Update hasn't flushed yet.
|
||||
ops.push('componentDidMount (after setState): ' + this.state.tick);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
ops.push('componentDidUpdate: ' + this.state.tick);
|
||||
if (this.state.tick === 2) {
|
||||
ops.push('componentDidUpdate (before setState): ' + this.state.tick);
|
||||
this.setState({ tick: 3 });
|
||||
ops.push('componentDidUpdate (after setState): ' + this.state.tick);
|
||||
// We're in a batch. Update hasn't flushed yet.
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
ops.push('render: ' + this.state.tick);
|
||||
instance = this;
|
||||
return <span prop={this.state.tick} />;
|
||||
}
|
||||
}
|
||||
|
||||
ReactNoop.render(<Foo />);
|
||||
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
expect(ops).toEqual([
|
||||
'render: 0',
|
||||
'componentDidMount (before setState): 0',
|
||||
'componentDidMount (after setState): 0',
|
||||
// If the setState inside componentDidMount were deferred, there would be
|
||||
// no more ops. Because it has Task priority, we get these ops, too:
|
||||
'render: 1',
|
||||
'componentDidUpdate: 1',
|
||||
]);
|
||||
|
||||
ops = [];
|
||||
instance.setState({ tick: 2 });
|
||||
ReactNoop.flushDeferredPri(20);
|
||||
|
||||
expect(ops).toEqual([
|
||||
'render: 2',
|
||||
'componentDidUpdate: 2',
|
||||
'componentDidUpdate (before setState): 2',
|
||||
'componentDidUpdate (after setState): 2',
|
||||
// If the setState inside componentDidUpdate were deferred, there would be
|
||||
// no more ops. Because it has Task priority, we get these ops, too:
|
||||
'render: 3',
|
||||
'componentDidUpdate: 3',
|
||||
]);
|
||||
});
|
||||
|
||||
it('invokes ref callbacks after insertion/update/unmount', () => {
|
||||
|
||||
var classInstance = null;
|
||||
|
||||
Reference in New Issue
Block a user