En una frase
React es una librería de JavaScript para construir interfaces de usuario, dividiéndolas en piezas de código autocontenidas y reutilizables llamadas «componentes», que se actualizan automáticamente cuando sus datos cambian.
El problema que resuelve
Imagina el desarrollo web en los viejos tiempos (digamos, a principios de la década de 2010). Tenías una página web y querías hacerla interactiva. Quizás querías que un botón de "me gusta" se pusiera azul y actualizara un contador al hacer clic. Usando una herramienta como jQuery, escribirías código que diría: "encuentra el botón en la página, escucha un clic y, cuando suceda, encuentra el elemento del contador y cambia su texto, luego encuentra el ícono del botón y cambia su color".
Este es un enfoque imperativo. Le estás dando al navegador una receta paso a paso sobre cómo cambiar la página. Funciona para un botón de "me gusta". Pero, ¿qué pasa con un feed completo de redes sociales con cientos de publicaciones, comentarios, notificaciones y una ventana de chat en vivo, todo actualizándose a la vez? Las "recetas" se convierten en un enredo de código espagueti. Un cambio en un lugar podría romper algo completamente diferente de forma impredecible. Mantener la UI consistente con los datos de la aplicación era una batalla constante y propensa a errores.
Facebook estaba viviendo esta pesadilla a gran escala. En 2011, crearon React para resolver este problema internamente. React introdujo un modelo declarativo. En lugar de decirle al navegador cómo cambiar las cosas, simplemente describes cómo debería verse la UI para un estado determinado. Tú declaras: "Si la publicación tiene liked, el botón debe ser azul y el contador debe ser 101. Si no tiene liked, el botón es gris y el contador es 100". Y ya está. Cuando el estado liked cambia de false a true, React se encarga de descifrar la forma más eficiente de actualizar el botón y el contador. Se encarga del «cómo» para que tú puedas enfocarte en el «qué».
Este cambio del pensamiento imperativo al declarativo fue una revolución. Hizo que la construcción de interfaces de usuario complejas y basadas en datos fuera más predecible, escalable y fácil de depurar.
Cómo funciona por debajo del capó
La «magia» de React no es realmente magia; es una combinación ingeniosa de tres ideas centrales: JSX, Componentes con Estado y el Virtual DOM.
JSX: JavaScript con Azúcar Sintáctico
Cuando miras el código de React, ves algo que se parece mucho a HTML incrustado directamente en JavaScript. Esto es JSX (JavaScript XML).
const greeting = <h1>¡Hola, usuario de FlowingDev!</h1>;
Tu navegador no tiene idea de qué hacer con esto. No es JavaScript válido. Antes de que este código pueda ejecutarse, necesita ser "transpilado" (transformado) por una herramienta como Babel. La línea de JSX de arriba se convierte en una llamada a una función normal de JavaScript.
const greeting = React.createElement("h1", null, "¡Hola, usuario de FlowingDev!");
Cada etiqueta JSX se convierte en una llamada a React.createElement(). Esta llamada de función no crea un elemento HTML real. Crea un objeto de JavaScript liviano que describe el elemento que quieres crear: su tipo (h1), sus propiedades (ninguna, en este caso, por lo que es null), y sus hijos ("¡Hola..."). Estos objetos son los bloques de construcción del Virtual DOM.
Entonces, JSX es solo "azúcar sintáctico". Nos permite describir nuestra UI con una sintaxis familiar, similar a HTML, pero por debajo, solo estamos creando un árbol de objetos de JavaScript.
Componentes y Estado: Los Cerebros de la Operación
Un componente es una función de JavaScript que devuelve algo de JSX. Es una pieza de UI reutilizable y autocontenida.
function Greeting({ name }) {
return <h1>¡Hola, {name}!</h1>;
}
Este componente Greeting es simple. Toma un name como entrada (llamado "prop") y renderiza un encabezado. Pero, ¿y si queremos que un componente recuerde cosas y cambie con el tiempo, como un contador? Para eso, necesitamos "estado".
El estado son datos que un componente "posee" y puede cambiar. React proporciona funciones especiales llamadas "hooks" para manejar esto. El más común es useState.
import { useState } from 'react';
function Counter() {
// "count" es nuestra variable de estado.
// "setCount" es la *única* función que podemos usar para actualizarlo.
const [count, setCount] = useState(0); // El valor inicial es 0.
return (
<div>
<p>Hiciste clic {count} veces</p>
<button onClick={() => setCount(count + 1)}>
Haz clic aquí
</button>
</div>
);
}
Cuando llamas a setCount(count + 1), no estás simplemente cambiando una variable. Le estás diciendo a React: "Oye, el estado de este componente Counter ha cambiado. Por favor, ejecuta esta función de nuevo para obtener la nueva UI y luego actualiza la pantalla". Entonces, React "re-renderiza" el componente, obtiene el nuevo JSX con el count actualizado y actualiza eficientemente la página web.
El DOM Virtual: El Arma Secreta de React
Cambiar directamente el DOM real del navegador (el árbol de elementos HTML vivo) es lento. Es como redecorar una habitación derribando todas las paredes para reconstruirlas, cuando solo querías mover un cuadro.
React evita este costoso trabajo con su arma secreta: el DOM Virtual (VDOM). El VDOM es solo una copia del DOM que se mantiene en la memoria de JavaScript. Es ese árbol de objetos de React.createElement del que hablamos.
Este es el proceso, conocido como reconciliación:
- Renderizado Inicial: Cuando
Counterse renderiza por primera vez, React construye un árbol de VDOM que describe undivque contiene unpcon el texto "Hiciste clic 0 veces" y unbutton. Luego usa esto para crear los elementos del DOM real en la página. - Cambio de Estado: Haces clic en el botón. Se llama a
setCount(1). - Re-renderizado y Nuevo VDOM: React vuelve a ejecutar la función
Counter. Construye un nuevo árbol de VDOM. Este nuevo árbol es casi idéntico al anterior, pero elpahora describe el texto "Hiciste clic 1 veces". - Diffing: El algoritmo de "diffing" de React compara el nuevo árbol del VDOM con el antiguo. Los recorre a toda velocidad y encuentra las diferencias. En este caso, encuentra solo una diferencia: el texto dentro de la etiqueta
<p>ha cambiado. - Actualización por Lotes (Batch Update): React toma este único y pequeño cambio y lo aplica al DOM real. No toca el
divni elbutton; solo actualiza el texto delp.
Este proceso de comparar y parchar (diff-and-patch) es increíblemente rápido. Al agrupar las actualizaciones y minimizar la manipulación directa del lento DOM real, React hace que las interfaces de usuario se sientan rápidas y responsivas, incluso cuando datos complejos están cambiando rápidamente.
Historias del mundo real
El Caso de los Datos del Formulario que Desaparecían
A un desarrollador junior se le encargó construir un complejo formulario de registro de usuarios de varios pasos. Cada paso era un componente diferente. En un momento, necesitaron un pequeño checkbox en el Paso 2 para mostrar condicionalmente un campo de entrada adicional. Lo implementaron, pero de repente, tuvieron un bug extraño: cada vez que un usuario marcaba esa casilla, todos los datos que habían ingresado en el Paso 2 se desvanecían.
El culpable fue un malentendido sobre las «keys». Para mostrar el campo adicional, el desarrollador estaba cambiando la estructura de su JSX de una manera que hacía que React pensara que todo el componente del formulario para el Paso 2 era un componente nuevo y diferente. Sin una key prop estable para identificarlo, React destruyó el componente antiguo (junto con todo su estado, es decir, la entrada del usuario) y montó uno completamente nuevo en su lugar.
La lección: La prop key no es solo para listas. Es una identidad estable para un componente. Entender cómo React usa las keys para rastrear componentes a través de los renderizados es crucial para preservar el estado y evitar reinicios inesperados.
El Martillo Infinito de APIs
Un equipo estaba construyendo un dashboard que mostraba datos de acciones en vivo. Un desarrollador escribió un componente para obtener el último precio de una acción específica usando el hook useEffect. El código se veía algo así:
useEffect(() => {
fetchStockPrice(stockSymbol).then(price => {
setPrice(price); // Actualiza el estado con el nuevo precio
});
}); // ¡Ups, falta el array de dependencias!
Tan pronto como el componente se cargó, la aplicación se congeló, el ventilador de la laptop del desarrollador se puso a mil y el monitoreo de red mostró miles de solicitudes de API idénticas disparándose por segundo. Habían creado un bucle infinito. El useEffect obtenía datos y llamaba a setPrice. La actualización del estado provocaba que el componente se re-renderizara. Como el useEffect no tenía un array de dependencias, se ejecutaba de nuevo después de cada renderizado, obteniendo los datos de nuevo, actualizando el estado de nuevo, causando otro re-renderizado, y así hasta el infinito.
La lección: Los hooks de React como useEffect son herramientas poderosas pero precisas. Debes proporcionar un "array de dependencias" para decirle a React cuándo volver a ejecutar el efecto. Omitirlo significa "ejecutar después de cada renderizado", lo cual es una receta para el desastre con las actualizaciones de estado.
Errores y trampas comunes
- Mutar el estado directamente. Nunca hagas
miEstado.prop = 'valor'omiArray.push('item'). La detección de cambios de React se basa en las identidades de objetos y arrays. Cuando llamas asetState, debes proporcionar un nuevo objeto o array (por ejemplo, usando la sintaxis de propagación...) para que React note el cambio. - Olvidar las
keys en las listas. Cuando renderizas una lista de elementos usando.map(), debes darle a cada elemento unakeyprop única y estable (comoitem.id). Así es como el algoritmo de diffing de React puede rastrear eficientemente qué elementos se han movido, agregado o eliminado. Usar el índice del array como clave es un anti-patrón común que puede llevar a bugs. - Tratar JSX como si fuera HTML. Es parecido, pero no es lo mismo. Tienes que usar
classNameen lugar declass,htmlForen lugar defor, y camelCase para las propiedades de estilo (backgroundColornobackground-color). Además, todas las etiquetas deben cerrarse, por lo que una imagen es<img ... />, no<img>. - Pensar que los hooks son magia. Hooks como
useStateyuseEffectsiguen reglas estrictas (por ejemplo, solo pueden ser llamados en el nivel superior de un componente de función). Funcionan confiando en un orden de llamada consistente entre renderizados. Intentar llamarlos dentro de bucles o condicionales romperá tu aplicación.
Por qué debería estar en tu radar
React no es solo una librería; es un ecosistema y una forma de pensar. Potencia una gran parte de la web moderna, desde sitios masivos como Instagram y Netflix hasta innumerables startups y herramientas internas. Aprender React significa aprender una arquitectura basada en componentes que promueve código reutilizable, comprobable y mantenible.
Incluso si no usas React a diario, entender sus principios fundamentales —UI declarativa, gestión del estado y el VDOM— te convertirán en un mejor desarrollador. Estos conceptos han influido en casi todos los demás frameworks de UI modernos, desde Vue hasta Svelte. Además, con React Native, las mismas habilidades y principios se pueden usar para construir aplicaciones móviles nativas para iOS y Android. Es un paradigma poderoso que es esencial para cualquier desarrollador que trabaje hoy en día con interfaces de usuario.
Para profundizar
- Documentación de React (Oficial) - La documentación oficial es el mejor lugar para empezar, con un tutorial completo y análisis profundos.
- Introducción a JSX (Docs de React) - La explicación oficial de JSX y en qué se diferencia de HTML.
- Reconciliación (Docs de React) - El algoritmo que usa React para comparar dos árboles del VDOM y actualizar la UI de manera eficiente.
- Las Reglas de los Hooks (Docs de React) - Las reglas esenciales que debes seguir al usar hooks como
useStateyuseEffect. - MDN: Introducción al DOM - Para entender qué es lo que React manipula, es vital entender el Document Object Model en sí.
Cómo entiende el código un editor
Los colores vienen de un analizador del lenguaje, no de ejecutar el programa. CodeMirror y Lezer reconocen los símbolos y la estructura dentro del navegador. Las sugerencias combinan palabras clave, nombres locales cuando el lenguaje lo permite, fragmentos y una pequeña referencia integrada; al pasar sobre un símbolo documentado aparece su firma. Una sugerencia no garantiza que esa API exista en el motor de ejecución. Los avisos del analizador detectan sintaxis mal formada cuando hay soporte, pero no sustituyen la comprobación de tipos ni la ejecución: un código bien escrito también puede fallar. Ruby y Lua usan análisis por tokens, con avisos más limitados. No hay un servidor remoto de lenguaje: el código no necesita salir del dispositivo.
CodeMirror & Lezer: MIT.