Skip to content

Memoizar a lista filtrada, os totais e os cartões de processo - #81

Merged
qzte merged 1 commit into
mainfrom
claude/loving-cori-68ipij
Sep 14, 2026
Merged

qzte merged 1 commit into
mainfrom
claude/loving-cori-68ipij

Conversation

@qzte

@qzte qzte commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Ponto #9 do plano de melhorias.

O que era recalculado a cada tecla

O filtered, o stats, o throughput30 e as médias de lead/cycle time viviam no corpo do App e eram refeitos a cada render — incluindo a cada tecla escrita na caixa de pesquisa, e a cada render provocado por qualquer outro estado.

Por processo, o filtro chama o calcStatus até quatro vezes e a ordenação chama o procOverallPct, que percorre todas as fases e passos dos dois percursos. Os totais são mais oito varrimentos da lista — e dependem só do procs, nada tendo a ver com o que está a ser escrito.

O que muda

  • Os quatro passam a useMemo, com as dependências que o react-hooks/exhaustive-deps valida (já estava ligado no lint, portanto isto é verificado e não prometido).
  • O ProcessCard passa a React.memo. É o que se multiplica pela lista, e cada cartão chama o procOverallPct e o calcStatus dos dois percursos ao desenhar-se. O onClick que recebe já era o setSelected — um setter de estado, estável entre renders —, pelo que a comparação de props não é anulada por uma função nova a cada render. Trocá-lo por uma arrow inline anula a memoização toda; fica escrito no código.
  • A pesquisa passa pelo useDeferredValue: a caixa responde à tecla e a lista é refeita logo a seguir, em vez de as duas coisas competirem pelo mesmo frame.
  • O isDone, o isLate e o roleStatus sobem para o nível do módulo (isProcDone, que já lá estava, isProcLate, procRoleStatus). Não dependiam de estado nenhum, e uma função recriada a cada render entra nas dependências do useMemo e fá-lo recalcular sempre — que é o mesmo que não o ter.

Medição, não estimativa

Com um backup gerado pela própria aplicação e carregado pelo «📂 Carregar backup», a escrever «Cardiologia» (11 teclas) na pesquisa. Sete repetições por célula, mediana, Chromium headless:

Processos main — escrita / lista estabiliza Este PR — escrita / lista estabiliza
300 218 ms / 222 ms 93 ms / 96 ms
800 451 ms / 455 ms 408 ms / 425 ms

A 300 processos é 2,3× mais rápido. A 800 a diferença é marginal (~7%) — aí o que domina é desenhar os cartões que realmente mudaram, e isso a memoização não remove. A dispersão das amostras a 800 também é grande (175–682 ms), pelo que esse número deve ser lido como «não piorou», não como ganho.

O backup actual tem 0 processos, portanto hoje nada disto se nota. É trabalho preventivo, e é a razão de o ter medido em vez de o afirmar.

O que deliberadamente não foi feito

Os 760 objectos style={{…}} ficam como estão. Objectos literais em props só custam quando o filho é memoizado, e o único que passou a sê-lo recebe apenas proc e onClick. Extraí-los todos seria um diff enorme, com risco visual real e ganho nulo.

Nota sobre o Date.now()

O «agora» do throughput passou para dentro do useMemo, pelo que é o do último procs e não o de cada render. A janela é de 30 dias: umas horas de diferença numa página deixada aberta não mudam nenhum dos números, e a gravação seguinte refaz a conta. Fica comentado no código.

Testes

npm run ci passa (86 unitários, lint, validate, build). Nos testes de browser, 62 passam e as 11 falhas são o ERR_CERT_AUTHORITY_INVALID do proxy TLS deste ambiente — o mesmo resultado, teste a teste, que antes desta alteração.

O tests/harness.mjs ganhou memo e useDeferredValue no seu React de mentira: ao contrário dos hooks, o memo é chamado na avaliação do módulo (const X = React.memo(…)) e sem ele o bloco JSX rebentava antes de definir seja o que for.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XwDMNpJzcA3Z2nP3jjwuJd


Generated by Claude Code

O `filtered`, o `stats`, o `throughput30` e as médias de lead/cycle time eram
recalculados a cada render do `App` — incluindo a cada tecla escrita na caixa de
pesquisa. Por processo, o filtro chama o `calcStatus` até quatro vezes e a
ordenação chama o `procOverallPct`, que percorre todas as fases e passos dos
dois percursos; os totais são mais oito varrimentos da lista, que dependem só do
`procs` e nada tinham a ver com o que estava a ser escrito.

  · os quatro passam a `useMemo`, com as dependências que o
    `react-hooks/exhaustive-deps` valida (já estava ligado no lint);
  · o `ProcessCard` passa a `React.memo`. É o que se multiplica pela lista e
    cada cartão chama o `procOverallPct` e o `calcStatus` ao desenhar-se. O
    `onClick` que recebe já era o `setSelected` — um setter de estado, estável
    entre renders —, pelo que a comparação de props não é anulada por uma
    função nova a cada render;
  · a pesquisa passa pelo `useDeferredValue`: a caixa responde à tecla e a
    lista é refeita logo a seguir, em vez de as duas coisas competirem pelo
    mesmo frame.

O `isDone`, o `isLate` e o `roleStatus` sobem para o nível do módulo, como
`isProcDone` (que já lá estava), `isProcLate` e `procRoleStatus`. Não dependiam
de estado nenhum, e uma função recriada a cada render entra nas dependências do
`useMemo` e fá-lo recalcular sempre — que é o mesmo que não o ter.

O `Date.now()` do throughput passou para dentro do `useMemo`, pelo que o "agora"
é o do último `procs` e não o de cada render. A janela é de 30 dias: umas horas
de diferença numa página deixada aberta não mudam nenhum número.

Os 760 objectos `style={{…}}` ficam como estão. Objectos literais em props só
custam quando o filho é memoizado, e o único que passou a sê-lo recebe apenas
`proc` e `onClick`. Extraí-los todos seria um diff enorme com risco visual e
ganho nulo.

O `tests/harness.mjs` ganha `memo` e `useDeferredValue` no seu React de
mentira: ao contrário dos hooks, o `memo` é chamado na avaliação do módulo
(`const X = React.memo(…)`) e sem ele o bloco JSX rebentava antes de definir
seja o que for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XwDMNpJzcA3Z2nP3jjwuJd
@qzte
qzte marked this pull request as ready for review September 14, 2026 20:43
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qzte
qzte merged commit bb528bf into main Sep 14, 2026
3 checks passed
@qzte
qzte deleted the claude/loving-cori-68ipij branch September 14, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants