BaliseTonSite

Bonnes Pratiques

Les habitudes qui font la différence entre un projet amateur et pro.

9.1Architecture de fichiers

Une structure claire, tu la comprends en un coup d'œil :

Bashbash
src/
├── components/
   ├── ui/              # Composants génériques (Button, Input, Modal)
   ├── layout/          # Header, Footer, Sidebar
   └── features/        # Composants métier (CartItem, ProductCard)
├── pages/               # Pages de l'app (Home, About, Dashboard)
├── hooks/               # Custom hooks
├── contexts/            # Providers Context
├── services/            # Appels API (fetch, axios)
├── types/               # Types TypeScript partagés
├── utils/               # Fonctions utilitaires
└── App.tsx

9.2Composants : petits et focalisés

Un composant ne devrait faire qu'une seule chose. Si tu dois scroller pour voir tout le composant, il est trop gros :

TSXtsx
// ❌ MAUVAIS : composant géant qui fait tout
function ProductPage() {
  // 20 lignes de state...
  // 15 lignes de useEffect...
  // 10 lignes de handlers...
  return (
    <div>
      {/* 200 lignes de JSX... */}
    </div>
  )
}

// ✅ BON : découpé en composants focalisés
function ProductPage() {
  const product = useProduct()
  return (
    <div>
      <ProductHeader product={product} />
      <ProductGallery images={product.images} />
      <ProductInfo product={product} />
      <AddToCartForm product={product} />
      <ProductReviews productId={product.id} />
    </div>
  )
}

9.3Custom Hooks : extraire la logique

Dès qu'une logique (state + effect) est réutilisable, extrais-la dans un hook custom :

TSXtsx
// hooks/use-fetch.ts - Hook réutilisable
import { useState, useEffect } from 'react'

export function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false

    async function fetchData() {
      try {
        setLoading(true)
        const res = await fetch(url)
        if (!res.ok) throw new Error(`Erreur ${res.status}`)
        const json = await res.json()
        if (!cancelled) setData(json)
      } catch (err) {
        if (!cancelled) setError(err instanceof Error ? err.message : 'Erreur')
      } finally {
        if (!cancelled) setLoading(false)
      }
    }

    fetchData()
    return () => { cancelled = true }
  }, [url])

  return { data, loading, error }
}

// Utilisation : ultra simple !
function Users() {
  const { data: users, loading, error } = useFetch<User[]>('/api/users')

  if (loading) return <p>Chargement...</p>
  if (error) return <p>Erreur : {error}</p>

  return users?.map(u => <p key={u.id}>{u.name}</p>)
}

9.4Performance : éviter les re-renders inutiles

React.memo

Empêche un composant de re-render si ses props n'ont pas changé. Utile pour les composants lourds (listes, graphiques).

useMemo / useCallback

Mémorise les calculs coûteux et les fonctions pour éviter de les recréer à chaque rendu.

Découper le state

Un gros state dans le parent cause le re-render de tous les enfants. Mets le state au plus proche de là où il est utilisé.

TSXtsx
// Règle d'or : state au plus proche
// ❌ State dans le parent → tout re-rend
function Page() {
  const [searchQuery, setSearchQuery] = useState("")
  return (
    <div>
      <SearchBar value={searchQuery} onChange={setSearchQuery} />
      <HeavyProductList />  {/* Re-rend à chaque frappe ! */}
      <Footer />             {/* Re-rend aussi ! */}
    </div>
  )
}

// ✅ State dans le composant qui l'utilise
function Page() {
  return (
    <div>
      <SearchBar />           {/* Gère son propre state */}
      <HeavyProductList />    {/* Tranquille */}
      <Footer />              {/* Tranquille */}
    </div>
  )
}

9.5Nommage et conventions

ÉlémentConventionExemple
ComposantsPascalCaseProductCard
Fichiers composantsPascalCase ou kebab-caseProductCard.tsx
HookscamelCase avec "use"useAuth, useFetch
Handlershandle + ActionhandleSubmit, handleClick
Props booleanis/has/can + AdjectifisOpen, hasError

9.6Checklist avant de publier

  • Tous les composants ont un nom descriptif
  • Pas de console.log restant en production
  • Les useEffect ont des cleanup quand nécessaire
  • Les listes ont des key uniques et stables
  • Le state est au plus proche de son utilisation
  • Les formulaires sont contrôlés et validés
  • Les erreurs sont gérées (try/catch, Error Boundaries)
  • L'app est responsive et accessible

Verifie tes acquis

3 questions pour valider ce chapitre

1. Quelle est la convention de nommage pour un custom hook ?

Valide et sauvegarde ce chapitre

Ne perds pas le fil de ton apprentissage. Chaque QCM terminé sauvegarde ton score. Crée ton profil gratuitement pour débloquer toutes les évaluations du site et retrouver tes résultats plus tard.

Commencer l'aventure
Déjà membre ?Connecte-toi