Bail out of updates in offscreen trees (#15666)

* Bail out of updates in offscreen trees

* Address review
This commit is contained in:
Dan Abramov
2019-05-16 11:12:05 +01:00
committed by GitHub
parent 4bf88ddeca
commit bb89b4eacc
3 changed files with 83 additions and 2 deletions
+72
View File
@@ -1290,6 +1290,78 @@ describe('ReactUpdates', () => {
expect(ops).toEqual(['Foo', 'Bar', 'Baz']);
});
it('delays sync updates inside hidden subtrees in Concurrent Mode', () => {
const container = document.createElement('div');
function Baz() {
Scheduler.yieldValue('Baz');
return <p>baz</p>;
}
let setCounter;
function Bar() {
const [counter, _setCounter] = React.useState(0);
setCounter = _setCounter;
Scheduler.yieldValue('Bar');
return <p>bar {counter}</p>;
}
function Foo() {
Scheduler.yieldValue('Foo');
React.useEffect(() => {
Scheduler.yieldValue('Foo#effect');
});
return (
<div>
<div hidden={true}>
<Bar />
</div>
<Baz />
</div>
);
}
const root = ReactDOM.unstable_createRoot(container);
root.render(<Foo />);
if (__DEV__) {
expect(Scheduler).toFlushAndYieldThrough([
'Foo',
'Foo',
'Baz',
'Foo#effect',
]);
} else {
expect(Scheduler).toFlushAndYieldThrough(['Foo', 'Baz', 'Foo#effect']);
}
const hiddenDiv = container.firstChild.firstChild;
expect(hiddenDiv.hidden).toBe(true);
expect(hiddenDiv.innerHTML).toBe('');
// Run offscreen update
if (__DEV__) {
expect(Scheduler).toFlushAndYield(['Bar', 'Bar']);
} else {
expect(Scheduler).toFlushAndYield(['Bar']);
}
expect(hiddenDiv.hidden).toBe(true);
expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
ReactDOM.flushSync(() => {
setCounter(1);
});
// Should not flush yet
expect(hiddenDiv.innerHTML).toBe('<p>bar 0</p>');
// Run offscreen update
if (__DEV__) {
expect(Scheduler).toFlushAndYield(['Bar', 'Bar']);
} else {
expect(Scheduler).toFlushAndYield(['Bar']);
}
expect(hiddenDiv.innerHTML).toBe('<p>bar 1</p>');
});
it('can render ridiculously large number of roots without triggering infinite update loop error', () => {
class Foo extends React.Component {
componentDidMount() {
+10 -1
View File
@@ -973,8 +973,8 @@ function updateHostComponent(current, workInProgress, renderExpirationTime) {
// Check the host config to see if the children are offscreen/hidden.
if (
renderExpirationTime !== Never &&
workInProgress.mode & ConcurrentMode &&
renderExpirationTime !== Never &&
shouldDeprioritizeSubtree(type, nextProps)
) {
// Schedule this fiber to re-render at offscreen priority. Then bailout.
@@ -2133,6 +2133,15 @@ function beginWork(
break;
case HostComponent:
pushHostContext(workInProgress);
if (
workInProgress.mode & ConcurrentMode &&
renderExpirationTime !== Never &&
shouldDeprioritizeSubtree(workInProgress.type, newProps)
) {
// Schedule this fiber to re-render at offscreen priority. Then bailout.
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
return null;
}
break;
case ClassComponent: {
const Component = workInProgress.type;
@@ -966,7 +966,7 @@ describe('ReactIncrementalSideEffects', () => {
// However, once we render fully, we will have enough time to finish it all
// at once.
expect(Scheduler).toFlushAndYield(['Bar', 'Bar', 'Bar']);
expect(Scheduler).toFlushAndYield(['Bar', 'Bar']);
expect(ReactNoop.getChildrenAsJSX()).toEqual(
<div>
<span prop={1} />