El JSX nos dio una forma natural de expresar interfaces: parece HTML y vive dentro de JavaScript. Eso facilitó la adopción y la lectura.
El problema no es la sintaxis, sino las suposiciones que construimos alrededor suyo: componentes que asumen demasiadas responsabilidades, re-renderings que se replican por toda la UI y efectos pegados al árbol visual.
Cuando la complejidad crece, estas decisiones de diseño pasan factura: rendimiento, testabilidad y capacidad de razonar sobre el comportamiento se resienten.
1. Componentes que se convierten en mini-aplicaciones
Los componentes comenzaron pequeños: recibían props y devolvían marcado. Hoy, muchos contienen:
- lógica de negocio,
- fetching de datos,
- efectos (suscripciones, animaciones),
- máquinas de estado,
- optimizaciones de rendimiento.
Eso genera acoplamiento y fragilidad: un cambio de estado profundo puede romper algo en otro nodo del árbol. La solución práctica es simple en idea y compleja en disciplina: dejar que los componentes definan estructura y apariencia, y mover la lógica reutilizable a servicios, stores o máquinas de estado.
Así las vistas son declarativas y fáciles de testear; la lógica se puede versionar y cubrir con pruebas unitarias.
2. La UI como “función del estado” choca con la realidad orientada a eventos
Tratar la UI como una función pura que se re-evalúa ante cualquier cambio funciona bien en ejemplos, pero en aplicaciones reales entran en juego eventos, latencia, inputs asíncronos y temporizaciones.
Re-ejecutar funciones enteras a menudo es trabajo innecesario. Por eso aparecen patrones y frameworks que rastrean dependencias con más precisión: cuando cambia un dato, solo el fragmento relevante se actualiza. Ventaja: menos re-render, menos sorpresas y un razonamiento mental más sencillo.
3. Efectos co-localizados = acoplamiento, no cohesión
Hooks y efectos trajeron visibilidad, pero también ataron ciclos de vida a la estructura visual. Eso dificulta extraer, reutilizar y probar lógica.
Pequeños errores en arrays de dependencias o en secuencias async producen bugs difíciles de reproducir. Mejor alternativa: gestionar efectos y flujos en servicios externos, stores o máquinas de estados, dejando al componente la responsabilidad de recibir estado y renderizarlo.
Esto facilita la auditoría, el logging y la seguridad (p. ej. mover flujos sensibles al backend).
4. Composición estructural sin depender del anidamiento del árbol
La aproximación clásica de envolver via providers o wrappers conduce a árboles muy profundos y dependencias implícitas.
Esto oculta la procedencia de datos y complica trazabilidad. Hoy hay opciones más maduras: valores reactivos independientes que componentes pueden suscribir sin necesidad de estar anidados.
El resultado: una jerarquía visual más plana y dependencias explícitas.
5. JSX no es el enemigo — su ejecución sí
JSX ofrece legibilidad, pero cuando el compilador la transforma en llamadas de función que se re-ejecutan, el modelo puede volverse ineficiente. Una alternativa conceptual es: tratar JSX como descripción estática enriquecida con bindings reactivos.
Con esa aproximación, el runtime no re-evalúa funciones enteras; actualiza solo los nodos afectados.
Enfoques (y ejemplos) que reevalúan la ejecución
- Compilación con bindings reactivos: frameworks que compilan componentes a código que actualiza el DOM de forma quirúrgica.
- Reactividad de grano fino: observar cambios en valores mínimos y actualizar solo lo necesario.
- Render resumible: reanudar la ejecución en el cliente sin tener que rehacer toda la lógica (evita la hidratación pesada).
Algunos proyectos que aplican estas ideas: Marko (streaming y compilación eficiente), Qwik (resumable), SolidJS (reactividad de grano fino) y Svelte (compilación a código imperativo). No es que JSX deba desaparecer: puede convivir con estos modelos si lo tratamos como descripción.
6. Recomendaciones prácticas para equipos
- Separa presentación y lógica: usa servicios, stores o máquinas de estado (XState, zustand, etc.).
- Prioriza reactividad fina donde importe: identifica hotspots de rendimiento y evalúa librerías con actualizaciones más precisas.
- Extrae efectos a capas externas: network, persistencia y side-effects fuera del cuerpo del componente.
- Evita árbol de wrappers innecesarios: favorece suscripciones directas a fuentes reactivas.
- Mide y prueba: incorpora métricas de render, snapshots y pruebas de integración que verifiquen comportamientos en lugar de implementaciones.
- Adopta cambios incrementalmente: empieza por módulos críticos; no reescribas toda la base de golpe.
Conclusión
Los componentes siguen siendo valiosos para organizar vistas, pero ya no deberían ser el centro absoluto del diseño arquitectónico.
Al desplazar la lógica hacia servicios, adoptar modelos de reactividad más precisos y tratar JSX como una descripción en lugar de un proceso, las aplicaciones ganan en robustez, rendimiento y mantenibilidad.
La próxima ola del front-end no abandona los componentes: los coloca en su papel correcto dentro de un sistema más amplio y coherente.