Apprenez à refactoriser des composants React avec Claude Code en utilisant des tests de caractérisation, de petits pas sûrs et le démêlage d'état pour améliorer la structure sans changer le comportement.

Les refactors React font peur parce que la plupart des composants ne sont pas de petits blocs propres. Ce sont des amas vivants d'UI, d'état, d'effets et de « encore une prop ». Quand vous changez la structure, vous modifiez souvent le timing, l'identité ou le flux de données sans le vouloir.
Un refactor change le comportement le plus souvent quand il :
Les refactors basculent aussi en réécritures quand le « nettoyage » se mêle aux « améliorations ». Vous commencez par extraire un composant, puis renommez plein de choses, puis « corrigez » la forme de l'état, puis remplacez un hook. Bientôt vous changez la logique en même temps que la mise en page. Sans garde-fous, difficile de savoir quel changement a causé le bug.
Un refactor sûr tient une promesse simple : les utilisateurs obtiennent le même comportement et le code est plus clair. Props, événements, états de chargement, erreurs et cas limites doivent se comporter pareil. Si le comportement change, que ce soit intentionnel, petit et bien signalé.
Si vous refactorez des composants React avec Claude Code (ou n'importe quel assistant), traitez-le comme un pair-programmer rapide, pas un pilote automatique. Demandez-lui de décrire les risques avant d'éditer, de proposer un plan en petites étapes sûres, et d'expliquer comment il a vérifié que le comportement restait identique. Puis validez vous-même : lancez l'app, parcourez les chemins bizarres et appuyez-vous sur des tests qui capturent ce que le composant fait aujourd'hui, pas ce que vous voudriez qu'il fasse.
Choisissez un seul composant qui vous coûte du temps. Pas toute la page, pas « la couche UI », et pas un vague « nettoyage ». Choisissez un composant difficile à lire, à modifier, ou plein d'état fragile et d'effets. Une cible précise rend aussi les suggestions de l'assistant plus faciles à vérifier.
Écrivez un objectif vérifiable en cinq minutes. Les bons objectifs portent sur la structure, pas sur les résultats : « diviser en composants plus petits », « rendre l'état plus lisible », ou « rendre testable sans mocker la moitié de l'app ». Évitez des objectifs comme « améliorer » ou « optimiser les performances » sauf si vous avez une métrique et un goulot identifié.
Définissez des limites avant d'ouvrir l'éditeur. Les refactors les plus sûrs sont ennuyeux :
Puis listez les dépendances qui peuvent casser discrètement le comportement quand vous déplacez du code : appels API, providers de context, params de routing, feature flags, analytics et état global partagé.
Exemple concret : vous avez un OrdersTable de 600 lignes qui fetch des données, filtre, gère la sélection et affiche un tiroir de détails. Un objectif clair peut être : « extraire le rendu des lignes et l'UI du tiroir en composants, et déplacer l'état de sélection dans un reducer, sans changements UI. » Cet objectif dit ce qu'« être terminé » signifie et ce qui est hors-scope.
Avant de refactoriser, traitez le composant comme une boîte noire. Votre travail est de capturer ce qu'il fait aujourd'hui, pas ce que vous souhaiteriez qu'il fasse. Cela empêche le refactor de devenir une refonte.
Commencez par écrire le comportement actuel en phrases simples : pour ces entrées, l'UI montre telle sortie. Incluez props, params d'URL, feature flags et toute donnée provenant du context ou d'un store. Si vous utilisez Claude Code, collez un petit extrait ciblé et demandez-lui de reformuler le comportement en phrases précises que vous pourrez vérifier plus tard.
Couvrez les états UI que les gens voient réellement. Un composant peut sembler correct sur le happy path tout en cassant dès qu'il rencontre loading, empty ou error.
Capturez aussi les règles implicites faciles à manquer et qui causent souvent des régressions :
Exemple : vous avez une table utilisateur qui charge des résultats, supporte la recherche et trie par « Last active ». Notez ce qui se passe quand la recherche est vide, quand l'API renvoie une liste vide, quand l'API échoue, et quand deux utilisateurs ont le même « Last active ». Notez aussi si le tri est insensible à la casse et si la table conserve la page courante quand un filtre change.
Quand vos notes deviennent ennuyeuses et spécifiques, vous êtes prêt.
Les tests de caractérisation décrivent « ce que c'est aujourd'hui ». Même si c'est incohérent ou étrange, ils empêchent un refactor de se transformer en réécriture silencieuse.
Quand vous refactorez des composants React avec Claude Code, ces tests sont vos garde-fous. L'outil peut aider à remodeler le code, mais c'est vous qui décidez ce qui ne doit pas changer.
Concentrez-vous sur ce dont dépendent les utilisateurs (et le reste du code) :
Pour garder les tests stables, affirmez les résultats plutôt que l'implémentation. Préférez « le bouton Enregistrer devient disabled et un message apparaît » à « setState a été appelé » ou « ce hook s'est exécuté ». Si un test casse parce que vous avez renommé un composant ou réordonné des hooks, il ne protégeait pas le comportement.
Le comportement asynchrone est l'endroit où les refactors changent souvent le timing. Traitez-le explicitement : attendez que l'UI soit stable, puis affirmez. S'il y a des timers (recherche débouncée, toasts retardés), utilisez des timers factices et avancez le temps. Pour les appels réseau, moquez fetch et affirmer ce que l'utilisateur voit après succès et après échec. Pour les flux de type Suspense, testez à la fois le fallback et la vue résolue.
Exemple : une table “Users” n'affiche “No results” qu'après la fin d'une recherche. Un test de caractérisation doit verrouiller cette séquence : indicateur de chargement d'abord, puis soit des lignes soit le message vide, quel que soit la façon dont vous scinderez plus tard le composant.
Le but n'est pas « plus de changements en moins de temps ». Le but est d'obtenir une image claire de ce que fait le composant, puis de changer une petite chose à la fois tout en gardant le comportement stable.
Commencez par coller le composant et demandez un résumé en anglais clair de ses responsabilités. Poussez pour des détails : quelles données il affiche, quelles actions utilisateur il gère, et quels effets secondaires il déclenche (fetchs, timers, subscriptions, analytics). Cela révèle souvent des tâches cachées qui rendent les refactors risqués.
Ensuite, demandez une carte des dépendances. Faites l'inventaire de chaque entrée et sortie : props, lectures de context, hooks custom, état local, valeurs dérivées, effets et helpers au niveau module. Une carte utile indique aussi ce qu'il est sûr de déplacer (calculs purs) versus ce qui est "collant" (timing, DOM, réseau).
Puis demandez des candidats à l'extraction, avec une règle stricte : séparez les morceaux purement visuels des parties contrôlées par l'état. Les sections lourdes en JSX qui ne nécessitent que des props sont d'excellents premiers extractions. Les sections qui mêlent handlers, appels asynchrones et mises à jour d'état généralement ne le sont pas.
Un workflow qui tient la route :
Les points de contrôle comptent. Demandez à Claude Code un plan minimal où chaque étape peut être commitée et revertie. Un point de contrôle pratique : « Extraire <TableHeader> sans changements logiques » avant de toucher le tri.
Exemple concret : si un composant affiche une table client, contrôle des filtres et fetch des données, extrayez d'abord le markup de la table (entêtes, lignes, état vide) dans un composant pur. Ce n'est qu'après que vous déplacerez l'état des filtres ou l'effet de fetch. Cet ordre empêche les bugs de voyager avec le JSX.
Quand vous scindez un gros composant, le risque n'est pas de déplacer du JSX. Le risque est de changer accidentellement le flux de données, le timing ou le câblage d'événements. Traitez l'extraction d'abord comme un exercice de copie-et-raccord, puis comme un nettoyage.
Commencez par repérer des frontières déjà présentes dans l'UI, pas dans la structure de fichiers. Cherchez des parties que vous pourriez décrire comme une « chose » en une phrase : un en-tête avec actions, une barre de filtres, une liste de résultats, un pied de page avec pagination.
Un premier mouvement sûr est d'extraire des composants présentatifs purs : props en entrée, JSX en sortie. Gardez-les volontairement ennuyeux. Pas de nouvel état, pas d'effets, pas de nouveaux appels API. Si le composant original avait un gestionnaire de clic qui faisait trois choses, laissez ce handler dans le parent et passez-le vers le bas.
Les limites sûres qui fonctionnent souvent : zone d'en-tête, liste et ligne item, filtres (inputs seulement), contrôles de pied (pagination, totaux, actions groupées), et dialogues (ouverture/fermeture et callbacks passés).
Le nommage importe plus qu'on ne croit. Choisissez des noms spécifiques comme UsersTableHeader ou InvoiceRowActions. Évitez des noms fourre-tout comme “Utils” ou “HelperComponent” qui masquent les responsabilités et favorisent le mélange de préoccupations.
N'introduisez un composant container que lorsqu'il y a un vrai besoin : un morceau d'UI qui doit posséder l'état ou les effets pour rester cohérent. Même dans ce cas, gardez-le étroit. Un bon container possède un seul but (par ex. « état des filtres ») et passe le reste via des props.
Les composants en désordre mélangent souvent trois types de données : état UI réel (ce que l'utilisateur a modifié), données dérivées (ce que vous pouvez calculer) et état serveur (ce qui vient du réseau). Traiter tout cela comme de l'état local rend les refactors risqués parce que vous pouvez changer quand les choses se mettent à jour.
Commencez par étiqueter chaque morceau de donnée. Demandez : l'utilisateur l'édite-t-il, ou puis-je le calculer à partir de props, d'état et de données fetchées ? Aussi : cette valeur est-elle possédée ici, ou seulement transmise ?
Les valeurs dérivées ne devraient pas vivre dans useState. Déplacez-les dans une petite fonction, ou un sélecteur mémoïsé quand c'est coûteux. Cela réduit les mises à jour d'état et rend le comportement plus prévisible.
Un pattern sûr :
useState.useMemo.Les effets cassent le comportement quand ils font trop ou réagissent aux mauvaises dépendances. Visez un effet par responsabilité : un pour sync avec localStorage, un pour fetch, un pour subscriptions. Si un effet lit beaucoup de valeurs, il cache souvent des responsabilités supplémentaires.
Si vous utilisez Claude Code, demandez un petit changement : séparez un effet en deux, ou déplacez une responsabilité dans un helper. Puis lancez les tests de caractérisation après chaque déplacement.
Faites attention au prop drilling. Le remplacer par du context n'aide que s'il supprime du wiring répété et clarifie la propriété. Un bon signe : le context reflète un concept d'application (utilisateur courant, thème, feature flags), pas un bricolage pour une branche de composants.
Exemple : un composant table peut stocker à la fois rows et filteredRows dans l'état. Gardez rows en state, calculez filteredRows à partir de rows + query, et placez le code de filtrage dans une fonction pure pour qu'il soit facile à tester et difficile à casser.
Les refactors tournent mal quand vous changez trop avant de vous en apercevoir. La solution est simple : travaillez par petits points de contrôle et traitez chaque point comme une mini-release. Même dans une seule branche, gardez des changements de taille PR pour pouvoir voir ce qui a cassé et pourquoi.
Après chaque mouvement significatif (extraction, changement de flux d'état), arrêtez-vous et prouvez que vous n'avez pas changé le comportement. Cette preuve peut être automatisée (tests) et manuelle (vérification rapide dans le navigateur). Le but n'est pas la perfection mais la détection rapide.
Boucle de point de contrôle pratique :
Si vous utilisez une plateforme comme Koder.ai, les snapshots et rollback agissent comme garde-fous pendant l'itération. Vous voulez quand même des commits normaux, mais les snapshots aident quand il faut comparer une version « connue bonne » ou quand une expérience tourne mal.
Tenez un petit carnet de vérification du comportement pendant que vous avancez. C'est simplement une note courte de ce que vous avez vérifié, et ça évite de re-vérifier sans cesse les mêmes choses.
Exemple :
Quand quelque chose casse, le carnet indique quoi revérifier et vos points de contrôle rendent le revert peu coûteux.
La plupart des refactors échouent de façons petites et ennuyeuses. L'UI semble fonctionner, mais une règle d'espacement disparaît, un handler se déclenche deux fois, ou une liste perd le focus pendant la saisie. Les assistants peuvent aggraver cela parce que le code paraît plus propre alors que le comportement dérive.
Une cause fréquente : changer la structure. Vous extrayez un composant et enveloppez avec un \u003cdiv\u003e supplémentaire, ou vous remplacez un \u003cbutton\u003e par un \u003cdiv\u003e cliquable. Les sélecteurs CSS, la mise en page, la navigation clavier et les queries de tests peuvent changer sans qu'on le remarque.
Les pièges qui cassent le plus souvent :
{} ou () => {}) peut déclencher des re-renders et réinitialiser l'état enfant. Surveillez les props qui étaient autrefois stables.useEffect, useMemo ou useCallback peut introduire des valeurs obsolètes ou des boucles si les dépendances varient. Si un effet s'exécutait « au clic », ne le transformez pas en quelque chose qui s'exécute « quand tout change ».Exemple concret : scinder un composant table et remplacer les keys des lignes par un index peut sembler OK, mais casser la sélection quand les lignes se réordonnent. Traitez le « propre » comme un bonus, et le « même comportement » comme l'exigence.
Avant de merger, vous voulez la preuve que le refactor a conservé le comportement. Le signal le plus simple est ennuyeux : tout fonctionne encore sans que vous ayez eu à « réparer » les tests.
Passez ce contrôle rapide après le dernier petit changement :
onChange se déclenche sur saisie utilisateur, pas au montage).Une vérification rapide : ouvrez le composant et suivez un chemin étrange, par ex. déclencher une erreur, retenter, puis vider les filtres. Les refactors cassent souvent les transitions même quand le chemin principal marche.
Si un point échoue, revenez au dernier changement et refaites-le en plus petit. C'est généralement plus rapide que déboguer un gros diff.
Imaginez un ProductTable qui fait tout : fetch des données, gère les filtres, contrôle la pagination, ouvre une confirmation de suppression, et gère les actions de ligne comme edit, duplicate et archive. Il a commencé petit, puis a grandi jusqu'à 900 lignes.
Symptômes familiers : état éparpillé dans des useState, quelques useEffect qui s'exécutent dans un ordre précis, et un changement « inoffensif » qui casse la pagination seulement quand un filtre est actif. Les gens arrêtent de toucher le code parce qu'il semble imprévisible.
Avant de changer la structure, verrouillez le comportement avec quelques tests de caractérisation React. Concentrez-vous sur ce que font les utilisateurs, pas sur l'état interne :
Vous pouvez maintenant refactorer en petits commits. Un plan d'extraction propre pourrait ressembler à : FilterBar rend les contrôles et émet les changements de filtre ; TableView rend les lignes et la pagination ; RowActions contient le menu d'actions et l'UI de confirmation ; et un hook useProductTable contient la logique pénible (params de query, état dérivé et effets).
L'ordre compte. Extraire d'abord l'UI bête (TableView, FilterBar) en transmettant les props inchangées. Gardez la partie risquée pour la fin : déplacer l'état et les effets dans useProductTable. Lors de cette étape, conservez les anciens noms de props et formes d'événements pour que les tests restent verts. Si un test casse, vous avez trouvé un changement de comportement, pas un problème de style.
Si vous voulez que refactoriser des composants React avec Claude Code devienne sûr à chaque fois, transformez ce que vous venez de faire en petit template réutilisable. Le but n'est pas plus de processus, mais moins de surprises.
Écrivez un petit playbook à suivre pour n'importe quel composant, même quand vous êtes fatigué ou pressé :
Conservez ce modèle comme snippet dans vos notes ou votre repo pour que le prochain refactor commence avec les mêmes garde-fous.
Une fois le composant stable et plus lisible, planifiez la passe suivante selon l'impact utilisateur. Un ordre fréquent : accessibilité d'abord (labels, focus, clavier), ensuite performance (mémoïsation, rendus coûteux), puis nettoyage (types, noms, code mort). Ne mélangez pas les trois dans une seule PR.
Si vous utilisez un workflow vibe-coding comme Koder.ai (koder.ai), le mode planning peut vous aider à détailler les étapes avant de toucher le code, et les snapshots/rollback servent de points de contrôle pendant l'itération. À la fin, exporter la source facilite la revue du diff final et maintient un historique propre.
Arrêtez quand les tests couvrent le comportement que vous craignez de casser, le prochain changement serait une nouvelle fonctionnalité, ou quand vous sentez l'envie de « tout perfectionner ». Si la séparation d'un grand formulaire a éliminé l'état enchevêtré et que vos tests couvrent la validation et la soumission, livrez. Mettez les idées restantes en backlog court pour plus tard.
Les refactors React changent souvent l'identité et le timing sans que vous le remarquiez. Les ruptures de comportement courantes incluent :
key a changé.Considérez tout changement structurel comme pouvant entraîner un changement de comportement tant que les tests ne prouvent pas le contraire.
Choisissez un objectif serré et vérifiable, axé sur la structure, pas sur des “améliorations”. Un bon objectif ressemble à :
Évitez des objectifs vagues comme « améliorer » sauf si vous avez une métrique et un goulot d'étranglement identifié.
Traitez le composant comme une boîte noire et décrivez ce que les utilisateurs peuvent observer :
Si vos notes vous semblent ennuyeuses et spécifiques, elles sont probablement utiles.
Ajoutez des tests de caractérisation qui décrivent ce que le composant fait aujourd'hui, même si c'est étrange.
Cibles pratiques :
Affirmez les résultats visibles dans l'UI, pas des appels internes de hooks.
Utilisez-le comme un pair-programmer prudent :
N'acceptez pas un diff de type grosse réécriture ; exigez des changements incrémentaux vérifiables.
Commencez par extraire des parties purement présentationales :
Copiez et raccordez d'abord ; nettoyez ensuite. Une fois l'UI divisée en sécurité, attaquez l'état/les effets par petites étapes.
Utilisez des clés stables liées à l'identité réelle (par ex. un ID), pas des indices de tableau.
Les clés index semblent « fonctionner » jusqu'à ce que vous triez, filtriez, insériez ou supprimiez des lignes — React réutilise alors de mauvaises instances et vous obtenez des bugs comme :
Si votre refactor change les clés, considérez-le comme hautement risqué et testez les cas de réordonnancement.
Ne mettez pas les valeurs dérivées dans useState quand vous pouvez les calculer.
Approche sûre :
Boucle de point de contrôle :
Sur des plateformes comme Koder.ai, les snapshots et rollback complètent les commits normaux lorsque l'expérience tourne mal.
Arrêtez lorsque le comportement est verrouillé et que le code est nettement plus facile à modifier. Signaux d'arrêt :
Livrez le refactor, puis consignez les suites (accessibilité, performance, nettoyage) comme tâches séparées.
filteredRowsrowsqueryuseMemo uniquement si le calcul est coûteuxCela réduit les comportements surprenants et facilite la compréhension du composant.