Skip to content

Commit 792afc2

Browse files
committed
docs: update startTransition async guidance for React 19
Clarify that useTransition supports async Actions in React 19, including state updates after await. Reserve the post-await wrapping requirement for standalone startTransition. Rephrase troubleshooting to avoid implying async callbacks are disallowed. Fixes #7172
1 parent 8bb31ac commit 792afc2

3 files changed

Lines changed: 22 additions & 12 deletions

File tree

src/content/reference/react/startTransition.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function TabContainer() {
4141

4242
#### Parameters {/*parameters*/}
4343

44-
* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React calls `action` immediately with no parameters and marks all state updates scheduled synchronously during the `action` function call as Transitions. Any async calls awaited in the `action` will be included in the transition, but currently require wrapping any `set` functions after the `await` in an additional `startTransition` (see [Troubleshooting](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](/reference/react/useTransition#preventing-unwanted-loading-indicators).
44+
* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). The function can be synchronous or asynchronous. React calls `action` immediately with no parameters and marks all state updates scheduled during the `action` as Transitions. State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](/reference/react/useTransition#preventing-unwanted-loading-indicators). If you need to track a pending Transition, use [`useTransition`](/reference/react/useTransition) instead.
4545

4646
#### Returns {/*returns*/}
4747

@@ -55,7 +55,7 @@ function TabContainer() {
5555

5656
* The function you pass to `startTransition` is called immediately, marking all state updates that happen while it executes as Transitions. If you try to perform state updates in a `setTimeout`, for example, they won't be marked as Transitions.
5757

58-
* You must wrap any state updates after any async requests in another `startTransition` to mark them as Transitions. This is a known limitation that we will fix in the future (see [Troubleshooting](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)).
58+
* When using the standalone `startTransition` function, state updates after `await` are not automatically marked as Transitions. Wrap them in another `startTransition` call (see [Troubleshooting](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). If you use [`useTransition`](/reference/react/useTransition) in React 19, the `startTransition` function it returns supports async Actions, and state updates after `await` are included in the Transition automatically.
5959

6060
* A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input state update.
6161

src/content/reference/react/useActionState.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Each time you call `dispatchAction`, React calls the `reducerAction` with the `a
103103
* `reducerAction` can be sync or async. It can perform sync actions like showing a notification, or async actions like posting updates to a server.
104104
* `reducerAction` is not invoked twice in `<StrictMode>` since `reducerAction` is designed to allow side effects.
105105
* The return type of `reducerAction` must match the type of `initialState`. If TypeScript infers a mismatch, you may need to explicitly annotate your state type.
106-
* If you set state after `await` in the `reducerAction` you currently need to wrap the state update in an additional `startTransition`. See the [startTransition](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition) docs for more info.
106+
* If you call `reducerAction` outside of a Transition (for example, directly in an event handler), wrap it in [`startTransition`](/reference/react/startTransition). See [Troubleshooting](/reference/react/useActionState#async-function-outside-transition) for more info.
107107
* When using Server Functions, `actionPayload` needs to be [serializable](/reference/rsc/use-server#serializable-parameters-and-return-values) (values like plain objects, arrays, strings, and numbers).
108108
109109
<DeepDive>

src/content/reference/react/useTransition.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function SubmitButton({ submitAction }) {
9595

9696
#### Parameters {/*starttransition-parameters*/}
9797

98-
* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React calls `action` immediately with no parameters and marks all state updates scheduled synchronously during the `action` function call as Transitions. Any async calls that are awaited in the `action` will be included in the Transition, but currently require wrapping any `set` functions after the `await` in an additional `startTransition` (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#perform-non-blocking-updates-with-actions) and [will not display unwanted loading indicators](#preventing-unwanted-loading-indicators).
98+
* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). The function can be synchronous or asynchronous. React calls `action` immediately with no parameters and marks all state updates scheduled during the `action` as Transitions, including updates after `await`. State updates marked as Transitions will be [non-blocking](#perform-non-blocking-updates-with-actions) and [will not display unwanted loading indicators](#preventing-unwanted-loading-indicators).
9999

100100
#### Returns {/*starttransition-returns*/}
101101

@@ -109,8 +109,6 @@ function SubmitButton({ submitAction }) {
109109

110110
* The function you pass to `startTransition` is called immediately, marking all state updates that happen while it executes as Transitions. If you try to perform state updates in a `setTimeout`, for example, they won't be marked as Transitions.
111111

112-
* You must wrap any state updates after any async requests in another `startTransition` to mark them as Transitions. This is a known limitation that we will fix in the future (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)).
113-
114112
* The `startTransition` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)
115113

116114
* A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input update.
@@ -1676,7 +1674,7 @@ startTransition(() => {
16761674
});
16771675
```
16781676
1679-
The function you pass to `startTransition` must be synchronous. You can't mark an update as a Transition like this:
1677+
The function you pass to `startTransition` is called immediately, but updates must be scheduled *during* that call. You can't mark an update as a Transition if you defer it with `setTimeout`:
16801678
16811679
```js
16821680
startTransition(() => {
@@ -1702,17 +1700,31 @@ setTimeout(() => {
17021700
17031701
### React doesn't treat my state update after `await` as a Transition {/*react-doesnt-treat-my-state-update-after-await-as-a-transition*/}
17041702
1705-
When you use `await` inside a `startTransition` function, the state updates that happen after the `await` are not marked as Transitions. You must wrap state updates after each `await` in a `startTransition` call:
1703+
In React 19, the `startTransition` function returned by `useTransition` supports async Actions. State updates after `await` are automatically included in the Transition:
1704+
1705+
```js
1706+
const [isPending, startTransition] = useTransition();
1707+
1708+
startTransition(async () => {
1709+
await someAsyncFunction();
1710+
// ✅ Updates after await are part of the Transition
1711+
setPage('/about');
1712+
});
1713+
```
1714+
1715+
This limitation only applies to the standalone [`startTransition`](/reference/react/startTransition) function. When you use it outside a component, state updates after `await` are not automatically marked as Transitions. Wrap them in another `startTransition` call:
17061716
17071717
```js
1718+
import { startTransition } from 'react';
1719+
17081720
startTransition(async () => {
17091721
await someAsyncFunction();
1710-
// ❌ Not using startTransition after await
1722+
// ❌ Not marked as a Transition with standalone startTransition
17111723
setPage('/about');
17121724
});
17131725
```
17141726
1715-
However, this works instead:
1727+
This works instead:
17161728
17171729
```js
17181730
startTransition(async () => {
@@ -1724,8 +1736,6 @@ startTransition(async () => {
17241736
});
17251737
```
17261738
1727-
This is a JavaScript limitation due to React losing the scope of the async context. In the future, when [AsyncContext](https://github.com/tc39/proposal-async-context) is available, this limitation will be removed.
1728-
17291739
---
17301740
17311741
### I want to call `useTransition` from outside a component {/*i-want-to-call-usetransition-from-outside-a-component*/}

0 commit comments

Comments
 (0)