R
Interview Deckreact · next.js · senior/staff
0·0·122
Avançado

Concurrent Features do React 18

useTransition, useDeferredValue, Suspense para data fetching. O que é renderização concorrente.

React 18 introduziu renderização concorrente: o React pode pausar, retomar e abandonar renderizações. Isso permite priorizar atualizações urgentes (digitação, clique) sobre não-urgentes (atualização de lista filtrada). useTransition marca updates como não-urgentes. useDeferredValue adia o processamento de um valor. Suspense para data fetching permite mostrar fallback enquanto aguarda dados.

Problema
Filtrar uma lista grande enquanto o usuário digita trava a UI: cada keystroke dispara re-render de toda a lista filtrada. O input fica lento e a UX ruim.
Solução
useTransition: marca a atualização da lista como transição (não-urgente). O input atualiza imediatamente, a lista filtra quando há tempo. isPending indica que há transição em andamento para mostrar indicador visual.
Tip
useTransition é para quando VOCÊ controla o estado. useDeferredValue é para quando você recebe um valor como prop ou de outra source e não pode adicionar startTransition na origem. Em geral: prefira useTransition. Use useDeferredValue quando não tem acesso ao setter.
// useTransition — filtro de lista pesada sem travar input
import { useState, useTransition } from 'react'

function SearchPage({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('')
  const [filtered, setFiltered] = useState(items)
  const [isPending, startTransition] = useTransition()

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    const value = e.target.value
    setQuery(value) // urgente — input atualiza imediatamente

    startTransition(() => {
      // não-urgente — pode ser interrompido por nova digitação
      setFiltered(items.filter(item =>
        item.name.toLowerCase().includes(value.toLowerCase())
      ))
    })
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending && <Spinner />}
      <List items={filtered} />
    </>
  )
}

// useDeferredValue — quando não controla o setter
function FilteredList({ query }: { query: string }) {
  const deferredQuery = useDeferredValue(query)
  // deferredQuery fica "atrás" de query durante digitação rápida
  const filtered = useMemo(
    () => items.filter(i => i.name.includes(deferredQuery)),
    [deferredQuery]
  )
  return <List items={filtered} />
}
Q.Qual a diferença entre useTransition e useDeferredValue?
A.useTransition: você controla quando o update é não-urgente — wrapping o setState com startTransition. useDeferredValue: você recebe um valor e cria uma versão "atrasada" dele — útil quando não controla a origem do update (prop de pai, etc). Ambos usam a mesma mecânica de concorrência.
Q.O que é Suspense para data fetching?
A.Permite que um componente "suspenda" sua renderização até que os dados estejam prontos, mostrando o fallback do Suspense mais próximo. React Query e SWR têm suporte experimental. Com Next.js App Router: `use(promise)` ou React Server Components fazem fetch suspense-aware nativamente. Simplifica loading states: um `<Suspense>` cobre múltiplos componentes filhos.
Q.Quando NÃO usar useTransition?
A.Para updates de input controlado — o valor do input deve ser sempre síncrono. Para animações — use CSS ou Framer Motion. Para feedback imediato que o usuário espera ser instantâneo. useTransition é para cálculos pesados que podem ser adiados, não para tudo.
81/122← →