-
Notifications
You must be signed in to change notification settings - Fork 47k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
useTransition should optionally also consider nested suspense boundaries #25082
Comments
This comment was marked as off-topic.
This comment was marked as off-topic.
@reneeichhorn Thanks for a well-written issue, and apologies for no response. I've read through it and I don't understand what behavior you want. Can you please describe what you want to happen when the button is pressed? |
My expectations from a user perspective, when clicking the "Fetch new collection" button, is that when the loading "spinner" disappears the UI is fully rendered and no new UI lazily pops in. So as a developer, since I'm using Perhaps the example is a bit too constructed but think of a ecommerce catalog page where a list of products are shown. When applying some filters (for example a brand filter) I don't want to present each product in the list individually but instead have one loading spinner that disappears once the subtree is done. From my current understanding this does not work right now because as seen in the example, new Suspense boundaries are added during the transition and React won't consider these. |
The canonical solution to this is to not have individual |
The reason we want to have Suspense boundaries around each product is because in our architecture the Product component only retrieves its UUID as a prop and then fetches its required data from the API. Now with While this works great already on the server this is not necessarily the behavior we want to have on the Client but at the same time we are "forced" to have the same Suspense boundaries set as the server to avoid hydration mismatches. From an UX perspective, this is how I'd imagine the behavior on the client when doing a setState causing content reload. |
What is the Suspense fallback for a product item? Is it an empty hole? A glimmer square? |
For us it is really just an empty hole although I'd also think a glimmering placeholder would look even better but that's not what we want to have for the UI as of now. |
@reneeichhorn just out of curiosity, how did you solve this / workaround this? |
@kurtextrem Instead we are doing "render-as-you-fetch", whereas the fetching part is controlled outside of React, and with that we built a custom logic that while all components within a subtree are being updated, collects state changes and puts them into a queue and only once the whole subtree is done, the "state change" queue is being flushed and that flush is what we wrap in You can read a bit more about it in our blog post we did some time ago: |
@reneeichhorn Appreciate it, thank you! |
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:
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.
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 to overcome the issue which do not work well for us.
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.
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.
The text was updated successfully, but these errors were encountered: