New feature: startTransition #41
Replies: 10 comments 31 replies
-
I know this is just a simplified example and data fetching isn’t the point, but isn’t this a data fetching anti-pattern since it implies that the data won’t be fetched until the render? I wonder if it would be possible to update the example to have best practices for data without derailing too much from the point, just to avoid anyone accidentally believing they should do this. |
Beta Was this translation helpful? Give feedback.
-
I like this explanation approach overall. I think it's going to be clearer and more understandable than the whole "multiple priorities" thing that's been floating around for the last couple years. |
Beta Was this translation helpful? Give feedback.
-
Is this function meant to be used in components just within a |
Beta Was this translation helpful? Give feedback.
-
What's the reason to include both |
Beta Was this translation helpful? Give feedback.
-
How do various transitions get orchestrated? Is it the order in which they occur? Or is there some consideration given to the changes necessary? |
Beta Was this translation helpful? Give feedback.
-
Is there anything functionally different between the following:
and
My intuition is that in the first instance, dependent1 and dependent2 are executed in tandem, so they'll always be updated at the same time; but in the second instance, they're executed independent from each other, so for example if dependent2 is extremely expensive, dependent1 can update faster (but still possibly slower than without |
Beta Was this translation helpful? Give feedback.
-
On naming: Dan mentioned in a twitter thread that
|
Beta Was this translation helpful? Give feedback.
-
While it's not a question, I want to repeat and highlight something Dan showed over in the React issues to help explain how let isInTransition = false
function startTransition(fn) {
isInTransition = true
fn()
isInTransition = false
}
function setState(value) {
stateQueue.push({
nextState: value,
isTransition: isInTransition
})
} |
Beta Was this translation helpful? Give feedback.
-
If there are multiple If there are multiple |
Beta Was this translation helpful? Give feedback.
-
One thing I found interesting in the old docs was the section about baking transitions into design systems / component libraries. I'm wondering if this is still the recommendation of the team after more experience integrating concurrent UI into Facebook.com, or whether leaving this decision to the application code has proven useful in some cases. Some questions to start with:
|
Beta Was this translation helpful? Give feedback.
-
Overview
In React 18 we’re introducing a new API that helps keep your app responsive even during large screen updates. This new API lets you substantially improve user interactions by marking specific updates as “transitions”. React will let you provide visual feedback during a state transition and keep the browser responsive while a transition is happening.
What problem does this solve?
Building apps that feel fluid and responsive is not always easy. Sometimes, small actions like clicking a button or typing into an input can cause a lot to happen on screen. This can cause the page to freeze or hang while all of the work is being done.
For example, consider typing in an input field that filters a list of data. You need to store the value of the field in state so that you can filter the data and control the value of that input field. Your code may look something like this:
Here, whenever the user types a character, we update the input value and use the new value to search the list and show the results. For large screen updates, this can cause lag on the page while everything renders, making typing or other interactions feel slow and unresponsive. Even if the list is not too long, the list items themselves may be complex and different on every keystroke, and there may be no clear way to optimize their rendering.
Conceptually, the issue is that there are two different updates that need to happen. The first update is an urgent update, to change the value of the input field and, potentially, some UI around it. The second, is a less urgent update to show the results of the search.
Users expect the first update to be immediate because the native browser handling for these interactions is fast. But the second update may be a bit delayed. Users don't expect it to complete immediately, which is good because there may be a lot of work to do. (In fact, developers often artificially delay such updates with techniques like debouncing.)
Until React 18, all updates were rendered urgently. This means that the two state states above would still be rendered at the same time, and would still block the user from seeing feedback from their interaction until everything rendered. What we’re missing is a way to tell React which updates are urgent, and which are not.
How does startTransition help?
The new
startTransition
API solves this issue by giving you the ability to mark updates as “transitions”:Updates wrapped in
startTransition
are handled as non-urgent and will be interrupted if more urgent updates like clicks or key presses come in. If a transition gets interrupted by the user (for example, by typing multiple characters in a row), React will throw out the stale rendering work that wasn’t finished and render only the latest update.Transitions lets you keep most interactions snappy even if they lead to significant UI changes. They also let you avoid wasting time rendering content that's no longer relevant.
What is a transition?
We classify state updates in two categories:
Urgent updates like typing, clicking, or pressing, need immediate response to match our intuitions about how physical objects behave. Otherwise they feel “wrong”. However, transitions are different because the user doesn’t expect to see every intermediate value on screen.
For example, when you select a filter in a dropdown, you expect the filter button itself to respond immediately when you click. However, the actual results may transition separately. A small delay would be imperceptible and often expected. And if you change the filter again before the results are done rendering, you only care to see the latest results.
In a typical React app, most updates are conceptually transition updates. But for backwards compatibility reasons, transitions are opt-in. By default, React 18 still handles updates as urgent, and you can mark an update as a transition by wrapping it into
startTransition
.How is it different from setTimeout?
A common solution to the above problem is to wrap the second update in setTimeout:
This will delay the second update until after the first update is rendered. Throttling and debouncing are common variations of this technique.
One important difference is that
startTransition
is not scheduled for later likesetTimeout
is. It executes immediately. The function passed tostartTransition
runs synchronously, but any updates inside of it are marked as “transitions”. React will use this information later when processing the updates to decide how to render the update. This means that we start rendering the update earlier than if it were wrapped in a timeout. On a fast device, there would be very little delay between the two updates. On a slow device, the delay would be larger but the UI would remain responsive.Another important difference is that a large screen update inside a
setTimeout
will still lock up the page, just later after the timeout. If the user is still typing or interacting with the page when the timeout fires, they will still be blocked from interacting with the page. But state updates marked withstartTransition
are interruptible, so they won’t lock up the page. They lets the browser handle events in small gaps between rendering different components. If the user input changes, React won’t have to keep rendering something that the user is no longer interested in.Finally, because
setTimeout
simply delays the update, showing a loading indicator requires writing asynchronous code, which is often brittle. With transitions, React can track the pending state for you, updating it based on the current state of the transition and giving you the ability to show the user loading feedback while they wait.What do I do while the transition is pending?
As a best practice, you’ll want to inform the user that there is work happening in the background. For that, we provide a Hook with an
isPending
flag for transitions:The
isPending
value is true while the transition is pending, allowing you to show an inline spinner while the user waits:The state update that you wrap in a transition doesn't have to originate in the same component. For example, a spinner inside the search input can reflect the progress of re-rendering the search results.
Why not just write faster code?
Writing faster code and avoiding unnecessary re-renders are still good ways to optimize performance. Transitions are complementary to that. They let you keep the UI responsive even during significant visual changes — for example, when showing a new screen. That's hard to optimize with existing strategies. Even when there's no unnecessary re-rendering, transitions still provide a better user experience than treating every single update as urgent.
Where can I use it?
You can use
startTransition
to wrap any update that you want to move to the background. Typically, these type of updates fall into two categories:startTransition
to keep the app responsive in the middle of an expensive re-render.We’ll be posting again soon to cover each of these use cases with specific examples. Let us know if you have any questions!
Beta Was this translation helpful? Give feedback.
All reactions