Architecture React : Patterns et bonnes pratiques
Les patterns React essentiels pour construire des applications maintenables et scalables.
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 :
tsxconst 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
Debug API par status code : 400, 403, 404, 413, 500 et le reste
Un seul guide pour debugger les erreurs HTTP (400, 403, 404, 413, 500…) : lire la famille du code, le détail exact, et chercher au bon endroit dès le départ.
{"TypeScript","JavaScript","Frontend"}TypeScript en 2025 : Pourquoi c'est devenu incontournable
Découvrez pourquoi TypeScript est devenu le standard de facto pour tout projet JavaScript sérieux en 2025.