From 3258a24f20948cc358ccf09b64f859ec09e773a4 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 8 Sep 2026 10:00:52 +0200 Subject: [PATCH] Estende il fix di useIo/useRosa a EventoCard e altri 13 usi non statistici EventoCard monta un hook per ogni card mostrata: usava useGiocatoreCorrente (= useIo = useRosa, le 6 statistiche pesanti) solo per leggere il proprio id. Il Calendario ne rende diverse insieme (prossimi eventi, compleanni, drawer del giorno), quindi ogni apertura moltiplicava il ricalcolo dell'intera rosa. Audit di tutti gli altri usi di useGiocatoreCorrente: 12 su 13 leggevano solo id/nome/verita', mai una statistica. Corretti allo stesso modo (-> useGiocatoreBase): benvenuto.tsx, VotazioneMvp, SondaggioCacche, VotoSocial, eventi.tsx, PromemoriaPalloni (Home), TurnoPalloni, ScoutEntry, Pagelle. partita.$id.tsx: `io` era dichiarato e mai piu' usato, rimosso. Stesso pattern trovato anche su useRosa (non solo useGiocatoreCorrente) in scout.tsx e RosaPresenze.tsx (montata su partita e allenamento): entrambi passati al nuovo useAnagraficaRosa, esteso con ruolo e numero. Unica eccezione: CelebrazioneBadge.tsx usa davvero le statistiche complete (badge, MVP, serie) ed e' montato globalmente in __root.tsx - non downgradabile, resta il costo di base piu' alto rimasto in giro. Co-Authored-By: Claude Sonnet 5 --- src/components/crapp/EventoCard.tsx | 7 ++- src/components/crapp/Pagelle.tsx | 5 +- src/components/crapp/PromemoriaPalloni.tsx | 5 +- src/components/crapp/RosaPresenze.tsx | 12 +++-- src/components/crapp/ScoutEntry.tsx | 5 +- src/components/crapp/SondaggioCacche.tsx | 5 +- src/components/crapp/TurnoPalloni.tsx | 5 +- src/components/crapp/VotazioneMvp.tsx | 5 +- src/components/crapp/VotoSocial.tsx | 5 +- src/lib/rosa.ts | 20 +++++--- src/routes/benvenuto.tsx | 5 +- src/routes/eventi.tsx | 5 +- src/routes/partita.$id.tsx | 2 - src/routes/scout.tsx | 10 ++-- todo.md | 59 +++++++++++++++++++--- 15 files changed, 112 insertions(+), 43 deletions(-) diff --git a/src/components/crapp/EventoCard.tsx b/src/components/crapp/EventoCard.tsx index e014d02..b63c3aa 100644 --- a/src/components/crapp/EventoCard.tsx +++ b/src/components/crapp/EventoCard.tsx @@ -16,7 +16,7 @@ import { formatData, statoMeta, type Stato } from "@/lib/crapp-data"; import type { Evento } from "@/lib/eventi"; import { useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { usePresenzeEvento, useSalvaPresenza } from "@/lib/presenze"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { dataOggi } from "@/lib/scout-live"; const tipoMeta = { @@ -69,7 +69,10 @@ export function EventoCard({ }) { const { risposte } = usePresenzeEvento(evento.id); const salva = useSalvaPresenza(); - const io = useGiocatoreCorrente(); + // Solo `io.id` serve qui (per leggere/scrivere la propria risposta): `useGiocatoreBase` + // legge la sola anagrafica, non le statistiche di tutta la rosa di `useGiocatoreCorrente`. + // Rilevante perché ogni card monta questo hook: il Calendario ne rende diverse insieme. + const io = useGiocatoreBase(); const { righe: squadra } = useGiocatoriSquadra(); const rosa = squadra.filter((g) => g.attivo); const stato = io ? risposte[io.id] : undefined; diff --git a/src/components/crapp/Pagelle.tsx b/src/components/crapp/Pagelle.tsx index f36c266..03309ba 100644 --- a/src/components/crapp/Pagelle.tsx +++ b/src/components/crapp/Pagelle.tsx @@ -5,7 +5,7 @@ import { cn } from "@/lib/utils"; import { Card } from "@/components/crapp/ui-bits"; import { Avatar } from "@/components/crapp/Avatar"; import type { Giocatore } from "@/lib/crapp-data"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { mieiVoti, pagellePartita, usePagelle, useVotaPagella } from "@/lib/pagelle"; const voti = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -20,7 +20,8 @@ export function Pagelle({ convocati: Giocatore[]; chiuse?: boolean; }) { - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const { voti: tutti, isPending } = usePagelle(); const vota = useVotaPagella(); const [apertoPer, setApertoPer] = useState(null); diff --git a/src/components/crapp/PromemoriaPalloni.tsx b/src/components/crapp/PromemoriaPalloni.tsx index 1819daa..f5067a8 100644 --- a/src/components/crapp/PromemoriaPalloni.tsx +++ b/src/components/crapp/PromemoriaPalloni.tsx @@ -3,11 +3,12 @@ import { formatData } from "@/lib/crapp-data"; import { eventiPalloni, eventoPrecedente, eventoSuccessivo, oggiISO } from "@/lib/palloni-core"; import { useTurniPalloni } from "@/lib/palloni"; import { useEventi } from "@/lib/eventi"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; /** Avvisi per chi è di turno: prendere i palloni oggi, o riportarli oggi. */ export function PromemoriaPalloni() { - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. Montato in Home. + const io = useGiocatoreBase(); const { turni } = useTurniPalloni(); const { eventi } = useEventi(); if (!io) return null; diff --git a/src/components/crapp/RosaPresenze.tsx b/src/components/crapp/RosaPresenze.tsx index 0cd1ad5..974c17d 100644 --- a/src/components/crapp/RosaPresenze.tsx +++ b/src/components/crapp/RosaPresenze.tsx @@ -7,8 +7,8 @@ import { Avatar } from "@/components/crapp/Avatar"; import { Barra } from "@/components/motion/Barra"; import { statoMeta, type Giocatore, type Stato } from "@/lib/crapp-data"; import { usePresenzeEvento, useSalvaPresenza } from "@/lib/presenze"; -import { useRosa } from "@/lib/rosa"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useAnagraficaRosa } from "@/lib/rosa"; +import { useGiocatoreBase } from "@/lib/user-store"; import { intestazioniAutenticate } from "@/lib/auth"; import { useIsAdmin } from "@/lib/ruoli"; import { dataOggi } from "@/lib/scout-live"; @@ -18,9 +18,11 @@ const ordine: Stato[] = ["presente", "ritardo", "forse", "infortunato", "assente export function RosaPresenze({ eventoId, data }: { eventoId: string; data: string }) { const { risposte, isPending } = usePresenzeEvento(eventoId); const salva = useSalvaPresenza(); - const io = useGiocatoreCorrente(); + // Solo `.id`/`.nome` servono qui: `useGiocatoreBase`/`useAnagraficaRosa` bastano, + // niente statistiche di squadra. + const io = useGiocatoreBase(); const admin = useIsAdmin(); - const rosa = useRosa(); + const rosa = useAnagraficaRosa(); const [sollecito, setSollecito] = useState(false); const passato = data < dataOggi(); @@ -169,7 +171,7 @@ function Gruppo({ }: { titolo: string; n: number; - lista: Giocatore[]; + lista: Array>; attenzione?: boolean; }) { return ( diff --git a/src/components/crapp/ScoutEntry.tsx b/src/components/crapp/ScoutEntry.tsx index c892bdb..6caa845 100644 --- a/src/components/crapp/ScoutEntry.tsx +++ b/src/components/crapp/ScoutEntry.tsx @@ -2,7 +2,7 @@ import { Link } from "@tanstack/react-router"; import { ChevronRight, Lock, Radio } from "lucide-react"; import { cn } from "@/lib/utils"; import { sessioneScaduta, usePartitaDiOggi, useSessioneScout } from "@/lib/scout-live"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; /** * Accesso allo scout live: attivo solo il giorno della partita e se nessun altro lo sta usando. @@ -17,7 +17,8 @@ export function ScoutEntry({ }) { const { pronto, partita: diOggi } = usePartitaDiOggi(); const partita = eventoId && diOggi?.id !== eventoId ? null : diOggi; - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const { data: sessione } = useSessioneScout(partita?.id ?? null); const attiva = sessione && !sessioneScaduta(sessione) ? sessione : null; diff --git a/src/components/crapp/SondaggioCacche.tsx b/src/components/crapp/SondaggioCacche.tsx index 96689e3..6f5dd4e 100644 --- a/src/components/crapp/SondaggioCacche.tsx +++ b/src/components/crapp/SondaggioCacche.tsx @@ -4,7 +4,7 @@ import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { Card } from "@/components/crapp/ui-bits"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { intestazioniAutenticate } from "@/lib/auth"; import { useIsAdmin } from "@/lib/ruoli"; import { @@ -27,7 +27,8 @@ export function SondaggioCacche({ dataEvento: string; oraEvento: string; }) { - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const { righe } = useCacche(); const salva = useSalvaCacche(); const { righe: squadra } = useGiocatoriSquadra(); diff --git a/src/components/crapp/TurnoPalloni.tsx b/src/components/crapp/TurnoPalloni.tsx index 42d78a5..066d082 100644 --- a/src/components/crapp/TurnoPalloni.tsx +++ b/src/components/crapp/TurnoPalloni.tsx @@ -7,14 +7,15 @@ import { intestazioniAutenticate } from "@/lib/auth"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; import { useAssegnaTurno, useTurniPalloni } from "@/lib/palloni"; import { useIsAdmin } from "@/lib/ruoli"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; export function TurnoPalloni({ eventoId }: { eventoId: string }) { const [aperto, setAperto] = useState(false); const [avviso, setAvviso] = useState(false); const { salvati, turni, isPending } = useTurniPalloni(); const assegna = useAssegnaTurno(); - const io = useGiocatoreCorrente(); + // Solo `.nome` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const admin = useIsAdmin(); const { righe: squadra } = useGiocatoriSquadra(); const rosa = squadra.filter((g) => g.attivo); diff --git a/src/components/crapp/VotazioneMvp.tsx b/src/components/crapp/VotazioneMvp.tsx index a19753c..55f79e6 100644 --- a/src/components/crapp/VotazioneMvp.tsx +++ b/src/components/crapp/VotazioneMvp.tsx @@ -3,7 +3,7 @@ import { Crown, Vote } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { usePresenzeEvento } from "@/lib/presenze"; import type { Evento } from "@/lib/eventi"; import { @@ -25,7 +25,8 @@ import { */ export function VotazioneMvp({ evento }: { evento: Evento }) { const matchId = evento.id; - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const voti = useVotiMvp(); const vota = useVotaMvp(); const { righe: squadra } = useGiocatoriSquadra(); diff --git a/src/components/crapp/VotoSocial.tsx b/src/components/crapp/VotoSocial.tsx index 1431dee..ccb21a8 100644 --- a/src/components/crapp/VotoSocial.tsx +++ b/src/components/crapp/VotoSocial.tsx @@ -3,7 +3,7 @@ import { Check, Crown, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { nomeCompleto, useGiocatoriSquadra } from "@/lib/giocatori-squadra"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { categorieSocial, conteggioCategoria, @@ -15,7 +15,8 @@ import { /** Voto social post-partita: un compagno per categoria, veloce da mobile. */ export function VotoSocial({ matchId }: { matchId: string }) { - const io = useGiocatoreCorrente(); + // Solo `.id` serve qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const voti = useVotiSocial(); const vota = useVotaSocial(); const { righe: squadra } = useGiocatoriSquadra(); diff --git a/src/lib/rosa.ts b/src/lib/rosa.ts index e8ceff3..2f5b3ea 100644 --- a/src/lib/rosa.ts +++ b/src/lib/rosa.ts @@ -25,18 +25,26 @@ function iniziali(nome: string, cognome: string): string { } /** - * Solo anagrafica (id, nome, data di nascita) dei giocatori attivi — es. per i - * compleanni nel Calendario. A differenza di `useRosa` non legge MVP, pagelle, - * cacche, palloni né infortuni: evita di montare quei cinque hook e il relativo - * `useMemo` solo per una data di nascita. + * Solo anagrafica (id, nome, ruolo, numero, data di nascita) dei giocatori attivi — es. + * per i compleanni nel Calendario o le liste presenze. A differenza di `useRosa` non + * legge MVP, pagelle, cacche, palloni né infortuni: evita di montare quei cinque hook e + * il relativo `useMemo` solo per l'anagrafica. */ -export function useAnagraficaRosa(): Array> { +export function useAnagraficaRosa(): Array< + Pick +> { const { righe: squadra } = useGiocatoriSquadra(); return useMemo( () => squadra .filter((g) => g.attivo) - .map((g) => ({ id: g.id, nome: nomeCompleto(g), nascita: nascitaPerId[g.id] ?? "" })), + .map((g) => ({ + id: g.id, + nome: nomeCompleto(g), + ruolo: g.ruolo, + numero: g.numero, + nascita: nascitaPerId[g.id] ?? "", + })), [squadra], ); } diff --git a/src/routes/benvenuto.tsx b/src/routes/benvenuto.tsx index 1b42ac2..c20cda0 100644 --- a/src/routes/benvenuto.tsx +++ b/src/routes/benvenuto.tsx @@ -10,7 +10,7 @@ import { useCollegaGiocatore, useGiocatoriSquadra, } from "@/lib/giocatori-squadra"; -import { impostaGiocatore, resetGiocatore, useGiocatoreCorrente } from "@/lib/user-store"; +import { impostaGiocatore, resetGiocatore, useGiocatoreBase } from "@/lib/user-store"; export const Route = createFileRoute("/benvenuto")({ validateSearch: (search: Record): { next?: string } => { @@ -38,7 +38,8 @@ export const Route = createFileRoute("/benvenuto")({ function Benvenuto() { const navigate = useNavigate(); const { next } = Route.useSearch(); - const giocatore = useGiocatoreCorrente(); + // Solo verità (`!!giocatore`): `useGiocatoreBase` (anagrafica) basta, niente statistiche. + const giocatore = useGiocatoreBase(); const { pronta, utenteId, emailUtente } = useSessione(); const { righe, daDatabase } = useGiocatoriSquadra(); const collega = useCollegaGiocatore(); diff --git a/src/routes/eventi.tsx b/src/routes/eventi.tsx index be29a12..c3b9477 100644 --- a/src/routes/eventi.tsx +++ b/src/routes/eventi.tsx @@ -25,7 +25,7 @@ import { type CategoriaEvento, type Evento, } from "@/lib/eventi"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useGiocatoreBase } from "@/lib/user-store"; import { useIsAdmin } from "@/lib/ruoli"; export const Route = createFileRoute("/eventi")({ @@ -57,7 +57,8 @@ const tipi: Array<{ id: CategoriaEvento; label: string }> = [ ]; function GestioneEventi() { - const io = useGiocatoreCorrente(); + // Solo verità (`!io`, gate admin): `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const admin = useIsAdmin(); const { eventi, isPending, isError, error, refetch } = useEventi(); const { righe: squadra } = useGiocatoriSquadra(); diff --git a/src/routes/partita.$id.tsx b/src/routes/partita.$id.tsx index d162f4d..e058097 100644 --- a/src/routes/partita.$id.tsx +++ b/src/routes/partita.$id.tsx @@ -12,7 +12,6 @@ import { SondaggioCacche } from "@/components/crapp/SondaggioCacche"; import { ScoutEntry } from "@/components/crapp/ScoutEntry"; import { useScoutMatches, totaliPerGiocatore, totaliSquadra } from "@/lib/scout-store"; import { csvScoutMatch, scaricaCsv } from "@/lib/scout-export"; -import { useGiocatoreCorrente } from "@/lib/user-store"; import { useIsAdmin } from "@/lib/ruoli"; import { VotazioneMvp } from "@/components/crapp/VotazioneMvp"; import { VotoSocial } from "@/components/crapp/VotoSocial"; @@ -46,7 +45,6 @@ export const Route = createFileRoute("/partita/$id")({ function PartitaDetail() { const { id } = Route.useParams(); const { evento } = useEvento(id); - const io = useGiocatoreCorrente(); const admin = useIsAdmin(); const scoutMatches = useScoutMatches(); const { data: csi } = useCsi(); diff --git a/src/routes/scout.tsx b/src/routes/scout.tsx index 994bb06..7cc4122 100644 --- a/src/routes/scout.tsx +++ b/src/routes/scout.tsx @@ -6,8 +6,8 @@ import { cn } from "@/lib/utils"; import { Card } from "@/components/crapp/ui-bits"; import { formatData } from "@/lib/crapp-data"; import type { Evento } from "@/lib/eventi"; -import { useRosa } from "@/lib/rosa"; -import { useGiocatoreCorrente } from "@/lib/user-store"; +import { useAnagraficaRosa } from "@/lib/rosa"; +import { useGiocatoreBase } from "@/lib/user-store"; import { usePresenzeEvento } from "@/lib/presenze"; import { statoIniziale, @@ -81,7 +81,8 @@ function Blocco({ function Scout() { const { pronto, partita } = usePartitaDiOggi(); - const io = useGiocatoreCorrente(); + // Solo `.id`/`.nome` servono qui: `useGiocatoreBase` basta, niente statistiche. + const io = useGiocatoreBase(); const sessione = useSessioneScout(partita?.id ?? null); const statoSalvato = useStatoScout(partita?.id ?? null); const apri = useApriSessioneScout(); @@ -204,7 +205,8 @@ function ScoutBoard({ const cancella = useCancellaStatoScout(); const salvaMatch = useSalvaScoutMatch(); const { risposte } = usePresenzeEvento(partita.id); - const rosa = useRosa(); + // Solo id/nome/numero servono (selezione e display): niente statistiche di squadra. + const rosa = useAnagraficaRosa(); const finito = useRef(false); /** In campo solo chi ha confermato la presenza (anche in ritardo). */ diff --git a/todo.md b/todo.md index d43eae7..34cee8d 100644 --- a/todo.md +++ b/todo.md @@ -137,17 +137,64 @@ Fix: nuovo hook `useAnagraficaRosa` in `rosa.ts` — solo `useGiocatoriSquadra` accetta ora `Pick[]` invece dell'intero `Giocatore[]`, riflettendo che è tutto ciò che usa. +## 11. [x] `EventoCard` ricalcolava la rosa intera una volta per card — APPLICATA + +Causa più probabile del lag "ancora presente" su Calendario dopo i punti 9-10: `EventoCard` +([EventoCard.tsx:72](src/components/crapp/EventoCard.tsx#L72)) usava `useGiocatoreCorrente` +(= `useIo` = `useRosa`, le 6 statistiche del punto 9) solo per leggere `io.id` — mai una +statistica. Il problema si moltiplica perché **ogni** `EventoCard` monta il proprio hook: il +Calendario ne renderizza diverse insieme (fino a 3 in "Prossimi eventi", altre in "Compleanni", +altre ancora nel drawer del giorno), quindi apriva Calendario = N ricalcoli indipendenti +dell'intera rosa con tutte le statistiche, non uno solo. Home ha lo stesso pattern (usa +`EventoCard` per "Prossimo impegno" e "Da confermare"). + +Fix: `EventoCard` usa ora `useGiocatoreBase` (sola anagrafica) invece di `useGiocatoreCorrente`. + +Nota: `useGiocatoreCorrente` è usato in altri 12 file (`PromemoriaPalloni`, `TurnoPalloni`, +`Pagelle`, `RosaPresenze`, `VotoSocial`, `VotazioneMvp`, `ScoutEntry`, `SondaggioCacche`, +`CelebrazioneBadge`, `partita.$id.tsx`, `eventi.tsx`, `scout.tsx`, `benvenuto.tsx`) — non +verificati singolarmente in questo giro. Se il lag emergesse altrove, controllare prima se +quell'uso legge davvero un campo statistico (allora serve `useIo`) o solo l'identità (allora +`useGiocatoreBase` basta), stesso ragionamento dei punti 9-11. + +## 12. [x] Stesso bug in altri 12 file — APPLICATA + +Audit di tutti gli altri usi di `useGiocatoreCorrente` (oltre `EventoCard`, punto 11): 12 su 13 +leggevano solo `.id`/`.nome`/verità, mai una statistica — stesso identico bug, ognuno però +montato una volta sola (non moltiplicato come in `EventoCard`). + +Corretti (→ `useGiocatoreBase`): `benvenuto.tsx`, `VotazioneMvp.tsx`, `SondaggioCacche.tsx`, +`VotoSocial.tsx`, `eventi.tsx`, `PromemoriaPalloni.tsx` (Home — impatto più alto del gruppo), +`TurnoPalloni.tsx`, `ScoutEntry.tsx`, `Pagelle.tsx`. + +`partita.$id.tsx:49`: `io` era dichiarato e mai più usato — rimosso del tutto (variabile morta, +nessun downgrade necessario). + +Trovati due bonus con lo stesso pattern ma sull'hook `useRosa` (non `useGiocatoreCorrente`), +sistemati nello stesso giro: +- `scout.tsx`: sia `io` (→ `useGiocatoreBase`) sia `rosa` (→ `useAnagraficaRosa`, usava solo + id/nome/numero per la selezione live). +- `RosaPresenze.tsx` (montata su partita **e** allenamento): stesso doppio fix. `useAnagraficaRosa` + esteso con `ruolo` e `numero` (oltre a id/nome/nascita) per coprire anche questo caso. + +L'unica eccezione confermata è `CelebrazioneBadge.tsx`, che usa realmente le statistiche +complete tramite `useNotificheSmart` — montato globalmente in `__root.tsx`, quindi resta il +costo di base più alto rimasto in giro, ma non è downgradabile: le servono davvero. + ## Note - `useMotoRidotto` (`lib/motion.ts`) è ora l'heuristic condiviso di "device debole" usato in `BottomNav.tsx`, `BarraSottosezioni.tsx` e `calendario.tsx` (punti 2, 3, 6). Se si riprende il punto 4 (coriandoli), conviene usare lo stesso hook invece di un check separato. -- Applicati: 1, 2, 3, 6, 7, 8, 9, 10. Restano da discutere/prioritizzare: 4 (coriandoli su - device medi), 5 (virtualizzazione liste lunghe). -- Pattern ricorrente (punti 9, 10): `useRosa()` è comodo ma calcola *tutte* le statistiche della - squadra; usarlo solo per identità/anagrafica (id, nome, nascita, iniziali) costa 5-6 hook e un - `useMemo` su tutta la rosa inutilmente. Se in futuro emerge un altro caso simile, riusare - `useAnagraficaRosa` (o `useGiocatoreBase` per il singolo giocatore) invece di `useRosa`/`useIo`. +- Applicati: 1, 2, 3, 6, 7, 8, 9, 10, 11, 12. Restano da discutere/prioritizzare: 4 (coriandoli + su device medi), 5 (virtualizzazione liste lunghe), e `CelebrazioneBadge.tsx` (usa + legittimamente le statistiche complete ma è montato su ogni pagina — non downgradabile, + eventualmente da rivedere con un intervento diverso, es. memoizzazione più aggressiva). +- Pattern ricorrente (punti 9, 10, 11): `useRosa()`/`useIo()`/`useGiocatoreCorrente()` sono + comodi ma calcolano *tutte* le statistiche della squadra; usarli solo per identità/anagrafica + (id, nome, nascita, iniziali) costa 5-6 hook e un `useMemo` su tutta la rosa inutilmente, e il + costo si moltiplica per ogni componente che lo monta (punto 11). Per la sola identità: + `useGiocatoreBase` (singolo giocatore) o `useAnagraficaRosa` (tutta la rosa). - Se il lag persistesse ancora dopo questi fix, il prossimo passo è profilare un device Android reale (Chrome DevTools remoto o `chrome://inspect`) invece di continuare a ipotizzare: a questo punto le cause "ovvie" lette dal codice sono coperte, e senza un trace reale si rischia