hardReact#50

useTransition for pending state

Prompt

Use useTransition to mark a slow filtering operation as non-urgent. The UI should remain responsive while the filter runs in the background. Show a "Loading..." indicator while the transition is pending.

Solution

export default function FilterList() {
  const [query, setQuery] = useState('')
  const [list, setList] = useState(Array.from({ length: 10000 }, (_, i) => `Item ${i}`))
  const [isPending, startTransition] = useTransition()
  const handleChange = (text) => {
    setQuery(text)
    startTransition(() => {
      setList(Array.from({ length: 10000 }, (_, i) => `Item ${i}`).filter(x => x.includes(text)))
    })
  }
  return (
    <View>
      <TextInput value={query} onChangeText={handleChange} />
      {isPending && <Text>Loading...</Text>}
      <Text>Results: {list.length}</Text>
    </View>
  )
}
Mentor's take

useTransition is React's concurrent scheduling exposed as an API. The problem it solves: typing into a filter over 10,000 rows makes every keystroke pay for a huge list render, so the input stutters. The fix is priority, not speed — split the update in two. setQuery(text) stays urgent: the keystroke echoes immediately. The expensive setList goes inside startTransition, marking it a low-priority lane.

What the scheduler does with that: the transition render runs in time slices on the JS thread, checking shouldYield() between fibers. When the next keystroke arrives mid-render, its urgent lane preempts — the half-finished transition render is thrown away and restarted with the fresh value, and because the render phase is side-effect-free, discarding it is safe. Nothing half-applied ever commits. isPending is true from the moment the transition is scheduled until it commits — that's your "Loading..." indicator, and it renders with the urgent update, so feedback is instant even while results lag.

Ordering matters: the urgent setQuery sits outside startTransition; wrap it too and the input itself goes low-priority, reintroducing the lag you were fixing.

Red flag: Saying the transition runs "in the background" or "on another thread." It's single-threaded time slicing — concurrency in React means interleaving, not parallelism. That one word is the difference between a senior and a junior answer here.

Say it: "startTransition marks an update as a low-priority lane: the keystroke commits urgently, the heavy render is interruptible and restartable, and isPending drives the feedback UI — it's cooperative time slicing, not a second thread."