Skip to content

10 — Render cost: virtualização ausente, memoização e re-render

Pergunta: mesmo com TTFB resolvido, o frame de pintura aguenta 2k linhas? Onde React re-renderiza além do necessário?

Evidência

  • rg "react-window|react-virtual" em src/zero matches. A plataforma não usa nenhuma técnica de virtualização hoje.
  • resultados-lista.tsx (747 LoC) renderiza a tabela completa retornada pelo backend. Com fetchAllRows drenando 2k linhas, são 2k×N nós DOM por mount.
  • Listas equivalentes em risco: mensagens_enviadas (timeline coord), alunos (gestão), inscricoes_olimpiada (subscrição em massa), logs_transacoes (admin-logs).

Por que isso virou problema só agora

Antes do fix do incidente Nível S, a query truncava em 1000 silenciosamente — a renderização também ficava capada. Com fetchAllRows, o DOM cresce na proporção real. Render cost virou tão relevante quanto fetch cost.

Onde virtualização é mandatória (proposta)

ComponenteCardinalidadeEstratégia recomendada
resultados-lista tabelaaté 8k linhas/olimpíada@tanstack/react-virtual em <tbody>
Timeline mensagens_enviadascrescente sem tetovirtual + paginação cursor por created_at
admin-logs tablecrescente sem tetovirtual + paginação cursor
gestao-alunos lista5k–10k em escolas grandesvirtual + filtro server-side

Nota: ao virar paginação servidor (F1), virtualização só é necessária se o pageSize ainda for ≥200. Para pageSize=50 (sugerido) o DOM cabe sem virtualizar — prefira F1 antes de virtualizar.

Re-render gaps a auditar

  • useMemo/useCallback em resultados-lista.tsx: derivar linhas filtradas/ordenadas dentro de useMemo([dados, filtros]), não em loose body.
  • Lista de itens deve receber key estável (UUID, não índice).
  • <Select> / dropdowns com 1k opções (séries, alunos) — confirmar Combobox virtual em vez de <SelectItem> plano.
  • Toda mutação que atualiza UMA linha não deve forçar refetch da lista inteira (usar setQueryData para patch otimista — cf. mem://architecture/optimistic-updates-standard).

Hipóteses

  • H1: Mesmo após F1 (paginação servidor) com pageSize=50, a tabela vai aguentar sem virtualização.
  • H2: A timeline de mensagens e admin-logs vão precisar de virtualização independentemente da paginação (UX de scroll infinito).
  • H3: Re-renderização em filtros (digitação no input de busca) é responsável por parte do "travado" percebido — investigar debounce em onChange antes de atribuir à query.

Próximas etapas (auditoria, não fix)

  • Medir com browser--performance_profile na tela Resultados antes e depois de F1.
  • Identificar todos os componentes que renderizam >200 itens via .map() direto.
  • Adicionar regra no 99: lista >300 linhas requer virtualização ou paginação servidor.