React con Claude Code en producción: componentes, Hooks, tests y revisión
Usa Claude Code para desarrollar React con componentes tipados, Hooks, pruebas, accesibilidad y revisión segura.
Claude Code puede acelerar mucho el desarrollo con React, pero en un equipo de producción la velocidad no basta. Lo importante es obtener cambios pequeños, revisables y comprobados. Si le pides “crea un dashboard bonito” sin límites, lo normal es terminar con demasiados componentes, demasiadas props y poca confianza.
Esta guía muestra un flujo práctico para React + TypeScript: límites de componentes, props, state, custom hooks, formularios, data fetching, pruebas con Testing Library, accesibilidad, rendimiento y prompts de revisión. Usa como base la documentación oficial de React, React Testing Library, MDN ARIA y Claude Code. Para seguir dentro de ClaudeCodeLab, revisa TypeScript tips, estrategias de testing, accesibilidad y optimización de rendimiento.
Define el contrato antes de generar
Props son entradas que un componente padre pasa a un hijo. State es lo que la interfaz recuerda. Un custom hook es una función reutilizable para lógica con estado o efectos. Un efecto incluye peticiones API, temporizadores o localStorage.
| Información | Ejemplo | Resultado |
|---|---|---|
| Responsabilidad | listar usuarios, filtrar, activar cuentas | evita componentes gigantes |
| Ubicación del estado | padre, URL, hook, cache de servidor | evita useState disperso |
| Datos | tipo User, respuesta API, estado vacío | props y tests concretos |
| Verificación | npm test, npm run build, teclado | evidencia para revisión |
Casos de uso reales
Caso uno: listas internas de administración, como usuarios, facturas, tickets o permisos. Claude Code puede dividir filtros, tabla, acciones por fila, estado vacío y error sin cambiar el contrato API.
Caso dos: formularios de perfil, contacto, checkout o inscripción a formación. Pídele que respete labels, validación, estado de envío y reintentos. Para formularios más complejos, consulta React Hook Form con Claude Code.
Caso tres: pantallas con datos del servidor, como búsqueda, notificaciones y dashboards. Separa server state de state temporal de UI. Para TanStack Query, lee TanStack Query; para estado cliente pequeño, compara Zustand.
Caso cuatro: revisión de código. En vez de pedir más UI, pide revisar el diff: límites de componentes, efectos innecesarios, accesibilidad, tests frágiles y renders evitables.
Diagrama de componentes
UserAdminPage
-> UserFilters: búsqueda y rol
-> UserTable: tabla y acciones
-> UserStatusBadge: solo estado visual
-> UserEditDialog: formulario
-> useUsers: fetch, refresh, error
Componente React tipado
Este ejemplo funciona en un proyecto Vite + React + TypeScript. No hace fetch; recibe datos y comunica eventos hacia arriba.
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 y test
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");
});
});
Prompt de revisión
Revisa este diff de React + TypeScript.
Comprueba límites de componentes, props excesivas, useEffect innecesario, mezcla de UI state y server state, labels y errores de formularios, cobertura con Testing Library y renders evitables en listas grandes.
No añadas librerías nuevas; propónlas solo si hace falta. Termina con comandos: npm test y npm run build.
Errores frecuentes
Los problemas más comunes son componentes demasiado pequeños sin valor real, useEffect para valores derivados, inputs sin label, botones de icono sin nombre accesible, errores comunicados solo por color y optimizaciones sin medición. Claude Code mejora cuando le pides “mínima abstracción útil” y “prueba desde el punto de vista del usuario”.
CTA y resultado
Para un equipo, documenta estas reglas en CLAUDE.md: límites, comandos de test, accesibilidad y patrones prohibidos de sobre-generación. ClaudeCodeLab ofrece formación y consultoría de Claude Code para aplicar este flujo a un repositorio React real, incluyendo revisión UI, Testing Library, rendimiento y formularios orientados a consulta o venta.
Resultado práctico: al usar este flujo en una UI administrativa de un sitio de contenidos, Masa redujo retrabajo porque Claude Code recibió límites, dueño del estado y expectativas de test antes de generar código. La pauta más estable fue generar diffs pequeños y verificables, no más componentes.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.