mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Bail out of updates in offscreen trees (#15666)
* Bail out of updates in offscreen trees * Address review
This commit is contained in:
@@ -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
@@ -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;
|
||||
|
||||
+1
-1
@@ -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} />
|
||||
|
||||
Reference in New Issue
Block a user