La programación reactiva es un paradigma declarativo basado en flujos de datos y la propagación de cambios. Para ilustrar su funcionamineto básico, consideremos un ejemplo simple donde las actualizaciones de variables no se reflejan automáticamente en los cálculos dependientes.
let precio = 5
let cantidad = 2
let resultado = precio * cantidad // Calcula el valor inicial: 10
precio = 20 // Actualizamos el precio
console.log(`Resultado: ${resultado}`) // Muestra 10, no se actualiza automáticamente
// El objetivo es que 'resultado' se convierta en 40 tras cambiar el precio
Para lograr que los cálculos se recalculen cuando cambian los datos, necesitamos almacenar la función de cálculo y ejecutarla en respuesta a las actualizaciones. Primero, definimos una función de registro y una de ejecución:
let precio = 5
let cantidad = 2
let resultado = 0
let objetivo = null
let almacen = []
objetivo = () => { resultado = precio * cantidad }
function registrar() {
almacen.push(objetivo)
}
function ejecutar() {
almacen.forEach(fn => fn())
}
registrar()
objetivo() // Ejecuta el cálculo inicial
precio = 20
console.log(resultado) // Muestra 10
ejecutar() // Recalcula
console.log(resultado) // Ahora muestra 40
Para mayor robustez, encapsulamos este comportamiento en una clase que implemente el patrón observador. Esto permite gestionar múltiples dependencias de manera estructurada.
class Dependencia {
constructor() {
this.observadores = []
}
suscribir() {
if (objetivo && !this.observadores.includes(objetivo)) {
this.observadores.push(objetivo)
}
}
notificar() {
this.observadores.forEach(obs => obs())
}
}
Con esta clase, simplificamos el proceso mediante una función observadora que configura la dependencia y ejecuta el cálculo:
function observar(funcionCalculo) {
objetivo = funcionCalculo
dep.suscribir()
objetivo()
objetivo = null
}
const dep = new Dependencia()
let precio = 5
let cantidad = 2
let total = 0
observar(() => {
total = precio * cantidad
})
console.log(total) // 10
precio = 20
console.log(total) // 10, aún no se actualiza
dep.notificar()
console.log(total) // 40, se actualiza tras notificar
Para aplicar la reactividad a múltiples propiedades de un objeto, usamos Object.defineProperty para interceptar accesos y modificaciones, vinculando cada propiedad a su propia instancia de dependencia.
let datos = { precio: 5, cantidad: 2 }
let objetivo = null
Object.keys(datos).forEach(clave => {
let valorInterno = datos[clave]
const dep = new Dependencia()
Object.defineProperty(datos, clave, {
get() {
dep.suscribir()
return valorInterno
},
set(nuevoValor) {
valorInterno = nuevoValor
dep.notificar()
}
})
})
function observar(funcionCalculo) {
objetivo = funcionCalculo
objetivo()
objetivo = null
}
let total = 0
observar(() => {
total = datos.precio * datos.cantidad
})
console.log(total) // 10
datos.precio = 20 // Dispara el setter y notifica
console.log(total) // 40
datos.cantidad = 3 // Dispara el setter y notifica
console.log(total) // 60
Este enfoque es fundamental en frameworks como Vue.js, donde cada propeidad reactiva tiene getter/setter que gestionan dependencias. Los componentes poseen observadores que se registran durante el cálculo y se actualizan cuando cambian los datos.