Repository: react
Issue: useTransition should optionally also consider nested suspense boundaries
Labels: Type: Discussion,Component: Suspense

React version: 18.2.0

## Background
We are working on a web framework that can be used to build highly personalized and data-driven applications. A page that is rendered by the framework consists of a tree of composable, declarative and self-sufficient widgets. A widget can be seen as a mini-application that specifies its own data dependencies and behavior. Since the widgets are self-sufficient, it gives us lots of flexibility to choose which widgets should be shown on page and how they are arranged. It allows us to have personalized pages or layouts per user depending on certain criteria, e.g. their browsing history. The way widgets are chosen to be displayed on the page can be seen as a dynamic, nested routing mechanism.

Here is an example of an outfit view that can be used in an ecommerce application:
![showing the structure of an example page with above described concept](https://img01.ztat.net/engineering-blog/posts/2021/09/images/rengine-outfit-page.png)

We are currently migrating the framework to React 18 with the new Suspense SSR and streaming architecture. The main idea is that we wrap each widget in its own suspense boundary to handle the loading of their code and data as soon as the framework decides to display it on the page. For initial requests it works well, but we face a problem with client-side updates that lead to new sub-tree of widgets.


## Steps to Reproduce
Here is a link to a code sandbox that contains a minimal code example to reproduce the problem with “new” suspense boundaries and useTransition: https://codesandbox.io/s/romantic-haslett-psc4f4?file=/src/App.js


## Current Behavior

useTransition waits for the first Suspense boundary to be ready and then re-renders the DOM although nested Suspense boundaries are still in their fallback state. It is similar to the problem that has been reported in [this issue](https://github.com/facebook/react/issues/24759).

In our code example, you can see a big loading overlay after clicking the button to fetch a new collection of widgets. Fetching a new collection of widgets means the following: determine which widgets should be displayed on the page and start loading their code and data. In a real application, this would be data-driven. In the code example, it has been simplified to randomly select a Square or Circle widget and lazy-load them via React.lazy().


## Expected Behavior

You have suggested [two approaches](https://github.com/facebook/react/issues/24759#issuecomment-1160605913) to overcome the issue which do not work well for us. 

> Why is it a problem that moving to a page shows the spinners? Is it because there are many of them?

Yes, in general there are many widgets and it might change to completely new page layouts. But the type of all widgets could also be the same as before the state update. Then it would be odd if the user first sees a big loading overlay for the whole page and when the new tree of widgets has been determined (which is the same as before, just the data changed), they will see individual loading spinners for each “new” widget.

> In that case, move the Suspense boundaries from individual dashboard items widgets to wrap all of them.

This seems to be impossible for us. A page consists of a tree of widgets. A widget can contain other widgets and each widget can trigger a state update. If a widget triggers a state update, it normally requests some data from backend services and that data determines which child widgets should then be displayed. These child widgets could have their own data dependencies and could again render other widgets, and so on. The whole code is very generic.

Our proposal is that useTransition should have an option to wait for the entire subtree to be ready, before re-rendering the component. In our use case, the widget of which the state should be updated, would use startTransition with some new flag that will additionally wait for the whole widget’s subtree to complete as well.