React 19's useTransition Looked Simple. Then I Found a Second Bug Hiding Inside the First One
Every hook in this series so far has been a passenger. useActionState rides inside a Transition and reports back the result. useOptimistic rides inside one and shows you a preview before the real answer lands. useFormStatus doesn't even ride, it just reads a status from whoever else is driving. useTransition is the only one of the four that sits in the driver's seat by itself, no form required,…
React 19's `useTransition` hook may seem simple at first, but hidden bugs can arise. This guide explains the intricacies of `useTransition`.
`useTransition` returns two values: `isPending` (a boolean) and `startTransition` (a function). `isPending` becomes `true` at the first call to `startTransition` and stays `true` until all actions in the transition complete. `startTransition` takes a single argument - a function to run immediately, with no delay or scheduling. Any updates within that function are marked as low priority and interruptible, rather than urgent.
Unlike `useActionState`, `useTransition` does not return a result value, built-in error state, or queue. The tradeoff is less structure, but also less standing between you and the raw mechanism.
Consider a search filter example: `query` updates synchronously, while `filterQuery` is wrapped in `startTransition`, causing a lag between `query` and `filterQuery`. The actual array iteration still runs synchronously, but the render that follows may be delayed if more urgent updates are occurring.
However, `query` cannot be wrapped in `startTransition`, only `filterQuery` can. If you don't control the setter for a controlled input, `useDeferredValue` is a better alternative. The decision to use `useDeferredValue` depends on who owns the `set` function.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.