El concepto Reactividad se utiliza muchísimo en programación web, y se define como la capacidad para responder de forma automática a cambios en los datos de una web. Se le suele llamar sistema reactivo a un sistema que es capaz de detectar esos cambios y actualizar sus elementos y componentes de forma que no tengas que hacerlo de forma manual.
Cuando trabajamos en Javascript nativo, lo normal es carecer de este tipo de reactividad. Debemos gestionar los datos por un lado, y cuando detectamos cambios en ellos, debemos realizar de forma manual los cambios en la interfaz visual. En general, por esta razón suele resultar un engorro trabajar en Javascript nativo. Los desarrolladores suele preferir los frameworks de Javascript porque tienen sistemas automáticos para detectar y actualizar sus elementos.
Sin embargo, Javascript nativo tiene ciertos sistemas para crear elementos reactivos sin necesidad de frameworks o librería, que de hecho, son las herramientas que usan estos frameworks para crear esa reactividad.
Sistema no reactivo
Javascript, por defecto, no es reactivo. Veamos un ejemplo simplificado de Javascript nativo donde tenemos datos y debemos actualizar de forma manual la interfaz de usuario:
let counter = 0;
function updateCounter(counter) {
const numberCounter = document.querySelector(".counter");
numberCounter.textContent = counter;
}
// Actualizamos datos e interfaz con nuevos datos
counter++;
updateCounter(counter);
A pesar de ser un ejemplo muy simple (que puede resultar sencillo), a medida que nuestro código crece y se hace más complejo y con más funcionalidades, modificar o hacer cambios en este código sin romper las funcionalidades de las que depende se vuelve un reto, sobre todo si tenemos que actualizar los cambios de forma manual.
Sistema reactivo
Nuestro objetivo es dotar de reactividad a nuestro sistema, de forma que permita actualizar los datos de la UI automáticamente, sin que nosotros tengamos que preocuparnos de ello y hacerlo manualmente. Esto facilitaría mucho la legibilidad y mantenibilidad del código:
// Creamos una variable reactiva
let counter = createReactiveData(0);
// Se actualizan los datos, y se actualiza la UI con esos datos
counter++;
La fuente de la verdad
La fuente de la verdad (The source of truth), a parte de tener un nombre de película de Indiana Jones, es un concepto mediante el cuál se conoce a una práctica donde debemos tener un único lugar donde se almacenan los datos reales de la aplicación.
Por ejemplo, nuestro contador almacenará sus datos (el número de contador) tanto en la variable counter
como en el DOM del HTML, sin embargo, la fuente de la verdad es la variable counter
, el resto de lugar donde aparezcan estos datos son lugares temporales que podrían estar desactualizados.
Aunque se podría elegir como opción, la fuente de la verdad no suele residir en el DOM de una aplicación web, ya que suele ser complejo gestionar esos datos. Lo más común es que esa información se encuentre separada de la aplicación, generalmente en una estructura de datos en memoria, de modo que sea fácil y eficiente acceder a esos datos, sencillo de escalar y predecible de depurar.
Por esta razón, nuestro objetivo suele ser tener unos datos por un lado (fuente de la verdad) y luego una interfaz de usuario que se va a actualizar cuando esos datos cambien, por lo que una característica deseable es que esos datos sean reactivos y se actualicen solos en la UI.
¿Cómo conseguir reactividad?
Quizás, una primera aproximación para conseguir un sistema reactivo, utilizando sólo herramientas básicas del lenguaje, sería crearnos una estructura de datos donde cada vez que cambie un dato, actualizar la interfaz de usuario con los datos que han cambiado. Algo similar a esto:
<div class="counter">-</div>
<button onclick="counter.decrement()">-</button>
<button onclick="counter.increment()">+</button>
<script>
const counterElement = document.querySelector(".counter");
class Counter {
counter;
constructor(initValue) {
this.counter = initValue;
this.update();
}
increment() {
this.counter++;
this.update();
}
decrement() {
this.counter--;
this.update();
}
update() {
counterElement.textContent = this.counter;
}
}
const counter = new Counter(0);
</script>
Sin embargo, ten en cuenta que esto no es un sistema reactivo. Se le acerca mucho, pero no lo es. La interfaz de usuario (DOM) se está actualizando llamando a update()
manualmente cuando hay cambios. Aunque conseguimos el resultado deseado, esto es algo que a la larga, cuando la aplicación sea mucho más compleja, probablemente pueda producir errores debido a un descuido o despiste, ya que la responsabilidad corre a cargo del programador.
Para conseguir reactividad lo ideal es tener una estructura de datos que al modificarla, se actualice por si sola la interfaz de usuario. Y eso se puede conseguir con algunas herramientas del lenguaje o externas al lenguaje.
Reactividad en Javascript
¿Cómo podemos entonces convertir nuestro sistema de Javascript nativo en algo reactivo? Aunque podríamos hacer nuestra propia implementación, probablemente más compleja, existen mecanicas y patrones, e incluso librerías, que pueden ofrecernos sistemas reactivos de modo que escribamos menos código para llegar a nuestro objetivo.
Veamos algunas de formas interesantes de conseguir reactividad:
Mecanismo | Categoría | Descripción |
---|---|---|
Object.defineProperty() | Nativo | Propiedades con getters/setters. Poco flexible. Base de Vue 2. |
Proxy | Nativo | Intercepta cambios y ejecuta lógica. Mejora del anterior. Base de Vue 3. |
MutationObserver | Nativo | Observa cambios en el DOM y ejecuta funciones al detectarlos. |
Custom Events | Nativo | Escucha y emite eventos para comunicar cambios sin acoplamiento. |
Signals | Propuesta | Estados reactivos de forma eficiente y nativa en Javascript. Base de Solid. |
RxJS | Librería externa | Programación reactiva basada en Observables. Base de Angular. |
MobX | Librería externa | Reactividad automática en objetos y estados. Basado en observables y proxies. |
Preact Signals | Librería externa | Implementación ligera de signals , inspirada en SolidJS. |
En los siguientes artículos vamos a indagar en estos mecanismos y a entender su funcionamiento.