thekof
Tous les articles
{"React","Architecture","Patterns"}01/02/2001true min

Architecture React : Patterns et bonnes pratiques

Les patterns React essentiels pour construire des applications maintenables et scalables.

Architecture React : Patterns et bonnes pratiques

Structurer un projet React comme un pro

Après 10+ projets React, j'ai identifié les patterns qui font la différence entre un projet maintenable et un plat de spaghetti. Voici mon approche éprouvée.

1. La structure de dossiers "Feature-First"

Oubliez l'organisation par type de fichier (components/, hooks/, utils/). Préférez l'organisation par fonctionnalité :

src/ ├── features/ │ ├── auth/ │ │ ├── components/ │ │ ├── hooks/ │ │ ├── services/ │ │ └── index.ts │ ├── dashboard/ │ │ ├── components/ │ │ ├── hooks/ │ │ └── index.ts │ └── projects/ ├── shared/ │ ├── components/ # Boutons, modals, inputs... │ ├── hooks/ # useDebounce, useLocalStorage... │ └── utils/ # formatDate, cn()... └── app/ ├── routes.tsx └── providers.tsx

Pourquoi ça marche ?

Chaque feature est un module autonome. Si vous devez supprimer la feature "blog", vous supprimez un dossier. Pas besoin de chasser des fichiers dans 15 dossiers différents.

2. Le pattern Compound Components

Au lieu de passer 20 props à un composant, utilisez le pattern compound :

tsx
// ❌ Trop de props, illisible <Card title="Mon projet" description="Un super projet" image="/img.jpg" actions={[{ label: 'Voir', onClick: () => {} }]} footer="2025" variant="outlined" /> // ✅ Compound Components — clair et flexible <Card> <Card.Image src="/img.jpg" /> <Card.Body> <Card.Title>Mon projet</Card.Title> <Card.Description>Un super projet</Card.Description> </Card.Body> <Card.Footer> <Card.Action onClick={() => {}}>Voir</Card.Action> </Card.Footer> </Card>

Implémentation :

tsx
const CardContext = createContext<{ variant?: string }>({}); function Card({ children, variant = 'default' }) { return ( <CardContext.Provider value={{ variant }}> <div className="card">{children}</div> </CardContext.Provider> ); } Card.Title = ({ children }) => <h3 className="card-title">{children}</h3>; Card.Body = ({ children }) => <div className="card-body">{children}</div>; Card.Image = ({ src }) => <img className="card-img" src={src} />;

3. Le pattern Custom Hook "Service"

Séparez votre logique métier de vos composants avec des hooks dédiés :

tsx
// hooks/useProjects.ts — toute la logique ici export function useProjects() { const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetchProjects().then(setProjects).catch(setError).finally(() => setLoading(false)); }, []); const addProject = async (data) => { /* ... */ }; const deleteProject = async (id) => { /* ... */ }; return { projects, loading, error, addProject, deleteProject }; } // components/ProjectList.tsx — uniquement du rendu function ProjectList() { const { projects, loading } = useProjects(); if (loading) return <Skeleton />; return projects.map(p => <ProjectCard key={p.id} project={p} />); }

4. Error Boundaries stratégiques

Ne mettez pas un seul Error Boundary au niveau de l'app. Placez-les stratégiquement :

tsx
<Layout> <ErrorBoundary fallback={<NavError />}> <Navbar /> </ErrorBoundary> <ErrorBoundary fallback={<ContentError />}> <Outlet /> </ErrorBoundary> <Footer /> {/* Pas d'error boundary — le footer ne crash jamais */} </Layout>

Conclusion

L'architecture n'est pas un luxe : c'est ce qui fait que votre projet survit au-delà du prototype. Adoptez ces patterns progressivement, et vous verrez la différence dès les premières itérations.

À lire ensuite