Skip to content

Commit

Permalink
Always bail out timed out children even if they receive an update (fa…
Browse files Browse the repository at this point in the history
…cebook#13901)

* Always bail out timed out children even if they receive an update

The fragment that wraps timed-out children should always have an
expiration time of NoWork.

* Don't need to set expirationTime, only childExpirationTime
  • Loading branch information
Andrew Clark authored and linjiajian999 committed Oct 22, 2018
1 parent 11431f7 commit ef32677
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ function updateSuspenseComponent(
));
fallbackChildFragment.effectTag |= Placement;
child = primaryChildFragment;
primaryChildFragment.childExpirationTime = NoWork;
// Skip the primary children, and continue working on the
// fallback children.
next = fallbackChildFragment;
Expand Down Expand Up @@ -1134,6 +1135,7 @@ function updateSuspenseComponent(
));
fallbackChildFragment.effectTag |= Placement;
child = primaryChildFragment;
primaryChildFragment.childExpirationTime = NoWork;
// Skip the primary children, and continue working on the
// fallback children.
next = fallbackChildFragment;
Expand Down Expand Up @@ -1435,6 +1437,7 @@ function beginWork(
const nextState = workInProgress.memoizedState;
const nextDidTimeout = nextState !== null && nextState.didTimeout;
if (nextDidTimeout) {
child.childExpirationTime = NoWork;
return child.sibling;
} else {
return child;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,5 +435,53 @@ describe('ReactSuspense', () => {
]);
expect(root).toMatchRenderedOutput('AB:2C');
});

it('bails out on timed-out primary children even if they receive an update', () => {
let instance;
class Stateful extends React.Component {
state = {step: 1};
render() {
instance = this;
return <Text text="Stateful" />;
}
}

function App(props) {
return (
<Suspense fallback={<Text text="Loading..." />}>
<Stateful />
<AsyncText ms={1000} text={props.text} />
</Suspense>
);
}

const root = ReactTestRenderer.create(<App text="A" />);

expect(ReactTestRenderer).toHaveYielded([
'Stateful',
'Suspend! [A]',
'Loading...',
]);

jest.advanceTimersByTime(1000);
expect(ReactTestRenderer).toHaveYielded(['Promise resolved [A]', 'A']);
expect(root).toMatchRenderedOutput('StatefulA');

root.update(<App text="B" />);
expect(ReactTestRenderer).toHaveYielded([
'Stateful',
'Suspend! [B]',
'Loading...',
]);

instance.setState({step: 2});

jest.advanceTimersByTime(1000);
expect(ReactTestRenderer).toHaveYielded([
'Promise resolved [B]',
'Stateful',
'B',
]);
});
});
});

0 comments on commit ef32677

Please sign in to comment.