Composables : la logique réutilisable
L'idée : une fonction qui retourne de l'état réactif
Un composable est une fonction useQuelqueChose() qui encapsule de la logique réactive et retourne des refs/fonctions :
// composables/useProgress.ts (celui de CETTE plateforme)
export function useProgress(courseSlug: string) {
const progress = useState<ProgressMap>(`progress:${courseSlug}`, () => ({}))
onMounted(async () => { /* fetch API ou localStorage, merge au login */ })
async function setStatus(path: string, status: Status) { /* optimistic + PUT */ }
return { progress, setStatus }
}
// N'IMPORTE quel composant :
const { progress, setStatus } = useProgress('ml-engineer')
Trois propriétés qui font la puissance du pattern :
- la composition : un composable peut en utiliser d'autres (
useProgressutiliseuseStateetuseLogtoUser) — la logique s'assemble comme des fonctions, sans hiérarchie de classes ; - l'état par appel : chaque composant qui appelle
useX()obtient SON instance d'état (sauf état partagé volontaire typeuseState) — pas de singleton accidentel ; - la testabilité : c'est une fonction — elle se teste sans monter de composant (nos tests de
diffLocalVsRemoteen sont l'exemple extrême : la logique pure extraite du composable).
Le cas d'école : les ~25 composables du front copilot
Le front du projet étudié découpe toute sa complexité en composables spécialisés : useCopilotSse (le parsing du stream), useCopilotMessages (l'historique affiché), useCopilotRun (le cycle de vie d'un run), useCopilotHitl (les approbations), useCopilotFrontendTools (les tools exécutés au navigateur)… Chacun une responsabilité, composés ensemble par la page. C'est la réponse front au « un fichier = une chose » du manifeste — et la raison pour laquelle un front de copilot temps réel reste maintenable.
Les règles d'hygiène
- préfixe
use, appelé au setup uniquement (pas dans un handler ou unif— les hooks de cycle de vie doivent s'enregistrer au bon moment) ; - retourne des refs, pas des valeurs (
return { count }et noncount.value— sinon la réactivité est perdue à la frontière) ; - l'I/O aux extrémités : le composable orchestre, les appels API vivent dans des fonctions dédiées — même règle que les services backend.
Un composable retourne des refs (et pas leurs .value) parce que…