React avec Claude Code en production : composants, Hooks, tests et revue
Développez React avec Claude Code de façon sûre : composants typés, Hooks, tests, accessibilité et revue.
Claude Code peut accélérer le développement React, mais une équipe de production ne cherche pas seulement à générer plus vite. Elle veut des changements petits, testables et faciles à relire. Sans limites claires, l’outil produit souvent trop de composants, trop de props et trop peu de preuves.
Ce guide présente un flux React + TypeScript : frontières de composants, props, state, custom hooks, formulaires, data fetching, tests avec Testing Library, accessibilité, performance et prompt de revue. Gardez les références officielles à portée de main : React, React Testing Library, MDN ARIA et Claude Code. Côté ClaudeCodeLab, complétez avec TypeScript, stratégies de test, accessibilité et performance.
Le contrat avant le code
Les props sont les entrées passées par un parent à un enfant. Le state est ce que l’interface retient. Un custom hook est une fonction réutilisable pour la logique avec état ou effets. Les effets couvrent les appels API, timers et accès au stockage.
| Information | Exemple | Pourquoi |
|---|---|---|
| Responsabilité UI | liste, filtre, activation | évite le composant géant |
| Emplacement du state | parent, URL, hook, cache serveur | évite les useState dispersés |
| Forme des données | type User, réponse API | rend les tests concrets |
| Vérification | npm test, npm run build, clavier | donne une preuve de qualité |
Cas d’usage réels
Premier cas : les listes d’administration pour utilisateurs, factures, tickets ou droits. Claude Code peut séparer filtres, tableau, actions de ligne, état vide et erreurs sans modifier le contrat API.
Deuxième cas : les formulaires de profil, contact, inscription ou paiement. Demandez-lui de préserver les labels, messages d’erreur, état d’envoi et reprise après échec. Pour aller plus loin, consultez React Hook Form.
Troisième cas : les écrans de données comme recherche, notifications et dashboards. Séparez server state et state temporaire d’interface. Voir TanStack Query et Zustand.
Quatrième cas : la revue de diff. Claude Code est très utile pour repérer composants trop larges, Effects inutiles, oublis d’accessibilité, tests fragiles et rendus excessifs.
Schéma de découpage
UserAdminPage
-> UserFilters: recherche et rôle
-> UserTable: tableau et actions
-> UserStatusBadge: affichage du statut
-> UserEditDialog: formulaire
-> useUsers: fetch, refresh, erreur
Si les props d’un composant ne se résument pas en une phrase, il fait probablement trop de choses. S’il n’est utilisé qu’une fois et ne porte aucun comportement, l’extraction est peut-être prématurée.
Composant React typé
import type { FormEvent } from "react";
export type UserRole = "admin" | "editor" | "viewer";
export type User = {
id: string;
name: string;
email: string;
role: UserRole;
active: boolean;
};
type UserTableProps = {
users: User[];
selectedRole: "all" | UserRole;
onRoleChange: (role: "all" | UserRole) => void;
onToggleActive: (id: string) => void;
};
export function UserTable({ users, selectedRole, onRoleChange, onToggleActive }: UserTableProps) {
const filteredUsers = selectedRole === "all" ? users : users.filter((user) => user.role === selectedRole);
function handleRoleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
onRoleChange(formData.get("role") as "all" | UserRole);
}
return (
<section aria-labelledby="user-table-title">
<h2 id="user-table-title">Team members</h2>
<form onSubmit={handleRoleSubmit} style={{ marginBottom: 12 }}>
<label htmlFor="role">Filter by role </label>
<select id="role" name="role" defaultValue={selectedRole}>
<option value="all">All</option>
<option value="admin">Admin</option>
<option value="editor">Editor</option>
<option value="viewer">Viewer</option>
</select>
<button type="submit">Apply</button>
</form>
{filteredUsers.length === 0 ? (
<p role="status">No users match this filter.</p>
) : (
<table>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Role</th>
<th scope="col">Status</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{filteredUsers.map((user) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.role}</td>
<td>{user.active ? "Active" : "Paused"}</td>
<td>
<button type="button" onClick={() => onToggleActive(user.id)}>
{user.active ? "Pause" : "Activate"}
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</section>
);
}
Hook, test et prompt
import { useEffect, useState } from "react";
import type { User } from "./UserTable";
type UsersState =
| { status: "loading"; users: User[]; error: null }
| { status: "success"; users: User[]; error: null }
| { status: "error"; users: User[]; error: string };
export function useUsers(endpoint: string) {
const [state, setState] = useState<UsersState>({ status: "loading", users: [], error: null });
useEffect(() => {
const controller = new AbortController();
async function loadUsers() {
setState({ status: "loading", users: [], error: null });
try {
const response = await fetch(endpoint, { signal: controller.signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const users = (await response.json()) as User[];
setState({ status: "success", users, error: null });
} catch (error) {
if (error instanceof DOMException && error.name === "AbortError") return;
setState({ status: "error", users: [], error: error instanceof Error ? error.message : "Unknown error" });
}
}
void loadUsers();
return () => controller.abort();
}, [endpoint]);
return state;
}
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { UserTable, type User } from "./UserTable";
const users: User[] = [
{ id: "1", name: "Masa", email: "masa@example.com", role: "admin", active: true },
{ id: "2", name: "Aiko", email: "aiko@example.com", role: "viewer", active: false },
];
describe("UserTable", () => {
it("filters users and toggles active status", async () => {
const user = userEvent.setup();
const onRoleChange = vi.fn();
const onToggleActive = vi.fn();
render(<UserTable users={users} selectedRole="all" onRoleChange={onRoleChange} onToggleActive={onToggleActive} />);
await user.selectOptions(screen.getByLabelText(/filter by role/i), "viewer");
await user.click(screen.getByRole("button", { name: /apply/i }));
await user.click(screen.getByRole("button", { name: /activate/i }));
expect(onRoleChange).toHaveBeenCalledWith("viewer");
expect(onToggleActive).toHaveBeenCalledWith("2");
});
});
Relis ce diff React + TypeScript.
Vérifie les frontières de composants, props excessives, useEffect inutile, mélange UI state/server state, labels et erreurs de formulaire, tests Testing Library, risques de re-render.
N’ajoute pas de bibliothèque. Termine par npm test et npm run build.
Pièges courants
Les pièges principaux sont la sur-génération de composants, les useEffect pour des valeurs dérivées, les formulaires sans labels, les boutons icônes sans nom accessible, les erreurs indiquées seulement par la couleur et les optimisations sans mesure. Demandez à Claude Code la plus petite abstraction utile et une preuve par test.
CTA et résultat
Pour une équipe, placez ces règles dans CLAUDE.md: découpage, commandes de test, accessibilité et limites d’abstraction. ClaudeCodeLab propose formation et conseil Claude Code pour appliquer ce flux à un vrai dépôt React, avec revue UI, Testing Library, performance et CTA de conversion.
Résultat observé : sur une interface d’administration de site de contenu, Masa a réduit les retours de revue en donnant d’abord à Claude Code les frontières, la propriété du state et les attentes de test. Le meilleur modèle reste un petit diff vérifiable, pas une génération massive.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.