Lista de verificación de refactor de UI
Un recorrido secuenciado para inspeccionar archivos de UI de React y Next.js y decidir qué refactorizar. Ejecuta los pasos en orden en los archivos dentro del alcance (una ruta, una carpeta de características o un diff de PR). Los pasos anteriores reformulan el área de superficie; los pasos posteriores ajustan lo que queda. Omite un paso solo si ya has verificado que está limpio.
Cómo usar esta lista de verificación
- Abre cada archivo en la característica o ruta a la vez:
app/<route>/page.tsx, sus componentes hijo, suactions.tsy cualquier hook colocado. - Haz un pequeño PR por paso que active. No agrupes una conversión de componente de servidor con una pasada de memoización.
- Si un paso no coincide, escribe una nota de una línea ("no se encontró
any") para que los revisores sepan que lo revisaste. - Detente después de los primeros 3-5 pasos de alto impacto si tienes un tiempo limitado: la lista está ordenada por influencia.
Pasada 1: Orientación
Obtén un mapa de los archivos antes de cambiar nada. La primera pasada es de solo lectura.
1. Mapea la ruta, luego el límite
Busca: ¿Qué archivo es la entrada de la ruta (page.tsx, layout.tsx)? ¿Cuáles hijo son componentes de servidor frente a cliente? ¿Dónde aparece el primer "use client"?
Refactoriza cuando: "use client" se encuentra en la parte superior de un contenedor que renderiza principalmente contenido estático. Empuja la directiva hacia la hoja interactiva más pequeña. Cada kilobyte de marcado estático por encima del límite se envía innecesariamente como JS.
// antes: toda la página es cliente
"use client";
export default function Page() {
return <Layout><Header /><Sidebar /><InteractiveChart /></Layout>;
}
// después: solo el gráfico es cliente
export default function Page() {
return <Layout><Header /><Sidebar /><InteractiveChart /></Layout>;
}
// InteractiveChart.tsx -> "use client";2. Enumera la responsabilidad única de cada componente
Busca: ¿Qué hace cada archivo en una frase? Si no puedes terminar la oración sin "y" u "también", está haciendo demasiado.
Refactoriza cuando: Un componente renderiza, obtiene datos, valida, formatea Y rastrea análisis. Divide en piezas enfocadas: un componente de servidor para datos, un componente de cliente para interacción, utilidades para formato.
3. Rastrea el flujo de datos
Busca: ¿Dónde entra cada pieza de datos en el árbol? Props, contexto, obtención de servidor, parámetro de ruta, parámetro de búsqueda de URL, tienda.
Refactoriza cuando: El mismo valor se obtiene en dos lugares, o props se pasan a través de 3+ componentes sin tocar (prop drilling). Mueve la obtención a un componente de servidor, o eleva el estado a un proveedor colocado, o compone con children en lugar de hacer drilling.
Pasada 2: Estructura
Reformula los límites de los archivos. Haz esto antes de ajustar tipos o memoizar: mover código primero hace esas pasadas más pequeñas.
4. Divide componentes dios (>200 líneas o >5 responsabilidades)
Busca: Un solo archivo con preocupaciones mixtas: marcado de encabezado, estado modal, lógica de obtención, formulario, tabla, pie de página.
Refactoriza: Extrae subcomponentes nombrados por su rol (OrderSummary, OrderItemList, OrderActions). Mantén cada uno bajo ~150 líneas.
5. Reemplaza prop drilling con composición o contexto
Busca: Una prop pasada a través de 3+ capas sin ser leída. O un componente que acepta 10+ props que se reenvían.
Refactoriza: Pasa children para que el padre proporcione la hoja directamente, o envuelve el subárbol en un proveedor de contexto definido para esa característica.
// antes: drilling de usuario 3 niveles profundos
<Page user={user}><Layout user={user}><Sidebar user={user} /></Layout></Page>
// después: composición
<Page><Layout><Sidebar user={user} /></Layout></Page>6. Mueve la obtención de datos fuera de useEffect
Busca: useEffect(() => { fetch(...).then(setData) }, []) en un componente que no necesita el navegador.
Refactoriza: Convierte el padre en un componente de servidor async y await los datos, o usa SWR / React Query para casos impulsados por cliente. useEffect para obtener datos es casi siempre incorrecto en código de App Router.
7. Convierte formularios en línea a acciones de servidor
Busca: <form onSubmit={...}> con fetch('/api/...') dentro del manejador.
Refactoriza: Define una acción de servidor en actions.ts, vinculala a través de <form action={submit}>, y usa useActionState para estado pendiente y de error. Mejora progresiva gratuita y sin desvío de validación de cliente.
8. Eleva el JSX duplicado a un componente compartido
Busca: Las mismas 8-15 líneas de marcado repetidas en 2-3 archivos (diseño de tarjeta, estado vacío, bloque de error).
Refactoriza: Extrae a un componente compartido en la misma carpeta de características. No sobre-abstraigas: si se usa dos veces con intención diferente, déjalo.
Pasada 3: Tipos y contratos
Una vez que la forma es correcta, bloquea los contratos.
9. Elimina any (y any implícito)
Busca: : any, as any, parámetros de función sin tipo, useState() sin tipo.
Refactoriza: Reemplaza con unknown en límites más una protección de tipo, o con el tipo exacto. useState<Foo | null>(null) en lugar de useState().
10. Reemplaza la sopa de props opcionales con uniones discriminadas
Busca: Props como { loading?: boolean; error?: Error; data?: T } donde solo uno se establece a la vez.
Refactoriza: Modela como { status: 'loading' } | { status: 'error'; error } | { status: 'success'; data }. Los estados imposibles se hacen imposibles.
11. Extrae valores mágicos a constantes nombradas
Busca: Números desnudos (< 3, 300, '#FF0000') y cadenas desnudas ('admin', 'pending') dispersas en JSX o manejadores.
Refactoriza: Eleva a una const en la parte superior del archivo o constants.ts compartido. Los nombres vencen a los comentarios.
12. Reemplaza enum con uniones as const
Busca: enum Status { ... } u objetos con claves de cadena que pretenden ser enumeraciones.
Refactoriza: const STATUS = ['idle','loading','success'] as const; type Status = typeof STATUS[number];. Bundle más pequeño, tipificación estructural, sin objeto de tiempo de ejecución.
Pasada 4: Corrección de renderizado
Atrapa los errores que se envían silenciosamente.
13. Corrige key={index} en listas dinámicas
Busca: items.map((item, i) => <Row key={i} ... />) donde la lista se reordena, filtra o se agregan elementos a mitad de la lista.
Refactoriza: Usa un id estable (item.id). Si no existe id, deriva un hash estable del contenido.
14. Elimina el estado derivado almacenado en useState
Busca: const [fullName, setFullName] = useState(\$ $`)` más un efecto que lo vuelva a sincronizar.
Refactoriza: Calcula en línea: const fullName = \$ $``. El estado debe ser entradas irreducibles, no sus proyecciones.
15. Audita arrays de dependencia de useEffect
Busca: Dependencias faltantes (advertencia del linter), o dependencias inestables (literales de objeto/función recreados en cada renderizado causando bucles infinitos).
Refactoriza: Memoiza la dependencia inestable con useMemo/useCallback, O mueve el valor fuera del renderizado, O colapsa el efecto en un manejador de eventos. Si el efecto solo está sincronizando un valor, consulta el paso 14.
16. Agrega o ajusta correctamente la memoización
Busca: Hijos pesados re-renderizándose en cada renderizado del padre (gráficos, tablas). O useMemo envolviendo a + b.
Refactoriza: Envuelve el hijo pesado en React.memo y estabiliza sus props. Elimina la memoización de cualquier cosa barata: useMemo en sí no es gratuito.
Pasada 5: Pulimiento
Última pasada. Estos son los pequeños triunfos que hacen que una característica se sienta terminada.
17. Reemplaza ternarios anidados con retornos anticipados u objeto de estado
Busca: cond1 ? a : cond2 ? b : cond3 ? c : d en JSX.
Refactoriza: Cambia según un campo status discriminado, o usa retornos anticipados: if (loading) return <Spinner />; if (error) return <Error />; return <Data />.
18. Agrega Suspense y límites de error en el grano correcto
Busca: Una ruta completa volviendo a un spinner, o un lanzamiento no capturado pantallazando la página en blanco.
Refactoriza: Agrega loading.tsx por segmento de ruta para streaming. Envuelve subárboles de cliente arriesgados en <ErrorBoundary> (o usa error.tsx de Next.js) para que el resto de la página sobreviva.
19. Elimina operaciones DOM imperativas a favor de estado declarativo
Busca: document.getElementById, el.classList.add, llamadas manuales de enfoque/desplazamiento dispersas en manejadores.
Refactoriza: Impulsa el comportamiento desde el estado y deja que React lo renderice. Usa ref + useEffect solo para necesidades imperativas genuinas (gestión de enfoque, librerías de terceros, medir diseño).
20. Audita accesibilidad y etiquetas visibles/invisibles
Busca: Botones sin texto o aria-label, controles solo de icono, falta de <label htmlFor>, estado de solo color, falta de alt en <Image>.
Refactoriza: Cada elemento interactivo necesita un nombre accesible. Cada imagen necesita alt (cadena vacía para decorativos). Cada entrada de formulario necesita una etiqueta asociada. Esta es la mejora de UX más barata de la lista.
Cuando hayas terminado
- Cada paso que activaste produjo un pequeño PR revisable.
- Cada paso que omitiste tiene una nota de una línea en la descripción del PR ("paso 12: sin enumeraciones en esta característica").
- Ejecuta la compilación, el conjunto de pruebas, y haz clic en la característica en el navegador antes de marcar la auditoría como completa.
- Si encuentras un patrón 21 que recurre en todo el repositorio, propón agregarlo a esta lista: la lista de verificación debe crecer con el repositorio.