Skip to content

🎯 Best Practices - Elerama Frontend ​

Questo documento raccoglie tutte le best practices aggiornate del progetto.


πŸ“‘ Data Fetching ​

βœ… USA TanStack Query (Raccomandato) ​

Per tutte le chiamate API, usa TanStack Query:

typescript
// βœ… RACCOMANDATO - TanStack Query
import { useCompaniesList } from "@/hooks/api/auth/useAuthApi";

const { data, isLoading, error, refetch } = useCompaniesList();
// Nessun useEffect, nessun useRef necessario!
// Cache automatica (5 min), de-duplicazione, StrictMode safe

Vantaggi:

  • βœ… Cache automatica (5 minuti default, configurabile)
  • βœ… StrictMode safe - De-duplicazione automatica delle richieste
  • βœ… Zero boilerplate - Nessun useEffect/useRef necessario
  • βœ… Invalidazioni coordinate - Mutations aggiornano automaticamente le query correlate
  • βœ… Override flessibile - Configurazione personalizzabile per ogni hook

❌ NON Usare useRef per Fetch ​

Con TanStack Query, NON serve piΓΉ useRef per prevenire doppi fetch in StrictMode:

typescript
// ❌ VECCHIO - Pattern deprecato
const hasFetchedRef = useRef(false);

useEffect(() => {
    if (!hasFetchedRef.current) {
        hasFetchedRef.current = true;
        fetchData();
    }
}, []);

// βœ… NUOVO - TanStack Query gestisce tutto automaticamente
const { data } = useCompaniesList();

Quando usare useRef:

  • βœ… BroadcastChannel (non-fetch use cases)
  • βœ… Riferimenti DOM (const inputRef = useRef<HTMLInputElement>(null))
  • βœ… Valori che non devono causare re-render
  • ❌ MAI per prevenire doppi fetch con TanStack Query

πŸ“š Documentazione TanStack Query ​

  • Guida Completa - Documentazione dettagliata (configurazione, hooks, best practices, troubleshooting)

πŸ“¦ Import Policy ​

❌ NO Barrel Files ​

NON creare mai file index.ts per re-export. Usa sempre import diretti:

typescript
// βœ… Corretto - Import diretto
import { useLogin } from "@/hooks/api/auth/useAuthApi";
import { authQueryKeys } from "@/hooks/api/auth/auth.query-keys";
import { invalidateAuthQueries } from "@/hooks/api/auth/auth.query-helpers";

// ❌ Sbagliato - Barrel file non esiste
import { useLogin } from "@/hooks/api/auth"; // ❌ No index.ts

Motivazione: Import diretti garantiscono:

  • TracciabilitΓ  del codice
  • Chiarezza delle dipendenze
  • Migliore tree-shaking
  • Nessuna ambiguitΓ 

πŸ” Route Protection ​

Protezione Base (Solo Autenticazione) ​

typescript
import { createProtectedLoader } from "@/lib/auth/loaders";

// βœ… Una riga per proteggere
export const clientLoader = createProtectedLoader();

export default function Dashboard() {
    return <div>Contenuto protetto</div>;
}

Protezione con Settings (Autenticazione + Dati Utente) ​

typescript
import { createProtectedLoader } from "@/lib/auth/loaders";

// βœ… Verifica autenticazione + presenza settings
// Se i settings non sono presenti, reindirizza a /welcome
export const clientLoader = createProtectedLoader({ requireSettings: true });

export default function Profile() {
    const userSettings = useAuthStore((state) => state.userSettings);
    // Qui sei sicuro che userSettings non Γ¨ null
    return <div>Profilo</div>;
}

Route Guest-Only (Solo Non Autenticati) ​

typescript
import { createGuestLoader } from "@/lib/auth/loaders";

// βœ… Reindirizza utenti giΓ  autenticati
export const clientLoader = createGuestLoader();

export default function Login() {
    return <div>Login</div>;
}

Route Pubbliche (Accessibili a Tutti) ​

typescript
// βœ… Nessun clientLoader = accessibile a tutti
export default function About() {
    return <div>Chi Siamo</div>;
}

πŸ“š Documentazione: Autenticazione


βš›οΈ React Compiler ​

Non Usare Memoizzazione di Default ​

React Compiler ottimizza automaticamente. NON usare useCallback, useMemo, React.memo di default:

typescript
// ❌ Non necessario - React Compiler ottimizza automaticamente
const handleClick = useCallback(() => {
    console.log("Clicked");
}, []);

// βœ… Lascia fare al Compiler
const handleClick = () => {
    console.log("Clicked");
};

Usa Memoizzazione SOLO Quando Necessario ​

USE ONLY when:

  1. Funzioni usate come dipendenze di useEffect
  2. Props passate a componenti third-party non compilati
  3. Scenari critici identificati con profiling
  4. Valori di Context che non devono causare re-render
typescript
// βœ… OK - useEffect dependency
const fetchData = useCallback(async () => {
    // API call
}, [dependency]);

useEffect(() => {
    fetchData();
}, [fetchData]);

πŸ“š Documentazione: react-compiler-verification.md


πŸ§ͺ Testing ​

Test con TanStack Query ​

Wrappa i test con QueryClientProvider:

typescript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";

test("useCompaniesList dovrebbe caricare le aziende", async () => {
    const queryClient = new QueryClient({
        defaultOptions: {
            queries: { retry: false },
        },
    });

    const { result } = renderHook(() => useCompaniesList(), {
        wrapper: ({ children }) => (
            <QueryClientProvider client={queryClient}>
                {children}
            </QueryClientProvider>
        ),
    });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));
    expect(result.current.data).toBeDefined();
});

πŸ“š Documentazione: testing.md


🎨 Component Patterns ​

Esempio Completo: Pagina con Data Fetching ​

typescript
import { createProtectedLoader } from "@/lib/auth/loaders";
import { AuthenticatedLayout } from "@/components/layout/authenticated-layout";
import { useCompaniesList } from "@/hooks/api/auth/useAuthApi";

// βœ… Protezione route
export const clientLoader = createProtectedLoader();

export default function Companies() {
    // βœ… TanStack Query - cache automatica
    const { data, isLoading, error, refetch } = useCompaniesList();

    if (isLoading) {
        return (
            <AuthenticatedLayout>
                <div>Caricamento...</div>
            </AuthenticatedLayout>
        );
    }

    if (error) {
        return (
            <AuthenticatedLayout>
                <div>
                    <p>Errore: {error.message}</p>
                    <button onClick={() => refetch()}>Riprova</button>
                </div>
            </AuthenticatedLayout>
        );
    }

    const companies = data?.data || [];

    return (
        <AuthenticatedLayout>
            <div className="p-8">
                <h1 className="text-3xl font-bold mb-4">Aziende</h1>
                <ul className="space-y-2">
                    {companies.map((company) => (
                        <li key={company.id} className="p-4 border rounded">
                            {company.name}
                        </li>
                    ))}
                </ul>
            </div>
        </AuthenticatedLayout>
    );
}

πŸ”„ Sincronizzazione Multi-Tab ​

Zustand Persist (Storage Events) ​

Sincronizzazione automatica dello stato tra tab tramite localStorage:

typescript
import { useAuthStore } from "@/store/auth/auth.store";

// Accesso ottimizzato con selector
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const userSettings = useAuthStore((state) => state.userSettings);

BroadcastChannel (Navigazione) ​

Per azioni che richiedono navigazione coordinata tra tab:

typescript
import { useBroadcastChannel } from "@/hooks/iframe/useBroadcastChannel";

const { sendMessage } = useBroadcastChannel("auth", (message) => {
    if (message.type === "logout") {
        navigate("/");
    }
});

// Logout con broadcast alle altre tab
const handleLogout = () => {
    authStore.getState().logout();
    sendMessage({ type: "logout" });
    navigate("/");
};

πŸ“š Documentazione: useBroadcastChannel.md


πŸ“ Documentation Policy ​

❌ NON Creare ​

  • Documenti di riepilogo per bug fix
  • Spiegazioni di singole modifiche
  • Change logs manuali

βœ… Aggiorna Sempre ​

  • Documentazione esistente quando le feature cambiano
  • Test quando il codice cambia
  • README quando cambia la struttura del progetto

πŸš€ Quick Checklist ​

Quando crei una nuova feature:

  • [ ] Route protection: Usa createProtectedLoader() se necessario
  • [ ] Data fetching: Usa TanStack Query hooks
  • [ ] NO useRef per fetch: TanStack Query gestisce StrictMode
  • [ ] Import diretti: NO barrel files (index.ts)
  • [ ] NO memoizzazione: Lascia fare a React Compiler (salvo casi specifici)
  • [ ] Test coverage: Scrivi test con QueryClientProvider wrapper
  • [ ] TypeScript strict: Tutti i tipi devono essere espliciti
  • [ ] Update docs: Aggiorna documentazione esistente se necessario


Ultima revisione: 2025-11-10 Versione TanStack Query: v5.90.7 React: 19 React Router: 7.9

Documentazione Elerama Frontend