Skip to content
Prev Previous commit
Next Next commit
[suspense][error handling] Add failing unit test
Covers an edge case where an error is thrown inside the complete phase
of a component that is in the return path of a component that suspends.
The second error should also be handled (i.e. able to be captured by
an error boundary.

The test is currently failing because there's a call to
`completeUnitOfWork` inside the main render phase `catch` block. That
call is not itself wrapped in try-catch, so anything that throws is
treated as a fatal/unhandled error.

I believe this bug is only observable if something in the host config
throws; and, only in legacy mode, because in concurrent/batched mode,
`completeUnitOfWork` on fiber that throws follows the "unwind" path
only, not the "complete" path, and the "unwind" path does not call
any host config methods.
  • Loading branch information
acdlite committed Sep 23, 2019
commit 6d5a2c293bc638472a8eafb9bbfebf3529f43c12
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,40 @@ describe('ReactSuspenseWithNoopRenderer', () => {
expect(Scheduler).toFlushExpired(['Hi']);
});
}

it('handles errors in the return path of a component that suspends', async () => {
// Covers an edge case where an error is thrown inside the complete phase
// of a component that is in the return path of a component that suspends.
// The second error should also be handled (i.e. able to be captured by
// an error boundary.
class ErrorBoundary extends React.Component {
state = {error: null};
static getDerivedStateFromError(error, errorInfo) {
return {error};
}
render() {
if (this.state.error) {
return `Caught an error: ${this.state.error.message}`;
}
return this.props.children;
}
}

ReactNoop.renderLegacySyncRoot(
<ErrorBoundary>
<Suspense fallback="Loading...">
<errorInCompletePhase>
<AsyncText ms={1000} text="Async" />
</errorInCompletePhase>
</Suspense>
</ErrorBoundary>,
);

expect(Scheduler).toHaveYielded(['Suspend! [Async]']);
expect(ReactNoop).toMatchRenderedOutput(
'Caught an error: Error in host config.',
);
});
});

it('does not call lifecycles of a suspended component', async () => {
Expand Down