[suspense][error handling] Inline renderRoot and fix error handling bug #16801
Follow-up to work loop refactor in #16743.
After the recent changes, the
Most of the changes here are copy-pasting and moving stuff around. The high level flow is essentially the same. I split the work into small commits to help with reviewing.
The final commit is unrelated, but I've included it because it fixes the test I added in #16800. The fix relies on the
ReactDOM: size: 0.0%, gzip: 0.0%
Details of bundled changes.
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.
I want to get rid of the the `isSync` argument to `renderRoot`, and instead use separate functions for concurrent and synchronous render. As a first step, this extracts the push/pop logic that happens before and after the render phase into helper functions.
Moving this out to avoid an accidental early return, which would bypass the call to `ensureRootIsScheduled` and freeze the UI.
Fatal errors (errors that are not captured by an error boundary) are currently rethrown from directly inside the render phase's `catch` block. This is a refactor hazard because the code in this branch has to mirror the code that happens at the end of the function, when exiting the render phase in the normal case. This commit moves the throw to the end, using a new root exit status.