EN

Mario Rocafull / Albin

Programador freelance en Valencia

9 agosto 2023 · Código Código

Reactividad sin frameworks

Cómo conseguir reactividad sin frameworks mediante el uso de los Proxy de Javascript.

En cuanto comprendes y practicas el concepto de reactividad en la interfaz de usuario, comienzas a verle muchas aplicaciones. He utilizado React para hacer una mediateca estilo Netflix y Preact para hacer el explorador de ficheros OPFS Explorer ¹

Como comenté en Lo más atractivo de preact para mi no es la velocidad lo que me gusta de Preact es que puedes aplicarlo para una funcionalidad en una zona de la pantalla sin necesidad de cargar con todo un framework, con transpilación, …

Pero creo que es importante dimensionar bien las necesidades, las comodidades, … y en cierta ocasión yo quería disfrutar de esa reactividad pero no estaba convencido de necesitar realmente todo lo que puede hacer por mi y de querer casar ese proyecto de por vida con los devenires que tenga Preact.

Así que estuve pensando cómo podría obtener una funcionalidad similar con poco código (yo suelo seguir mucho el principio KISS).

Aquí quiero presentar mi propia versión de signals (le tomo el nombre prestado).

  • Basado en Proxy. Está basado en los Proxy nativos de Javascript para interceptar cuándo se modifica una propiedad.
  • El valor es simpre un objeto. En consecuencia de lo anterior, el valor ha de ser siempre un objeto. Si solo quieres almacenar un valor primitivo puedes utilizar la propiedad .value como harías igualmente en una signal original. Como ventaja adicional, este objeto puede tener métodos propios para modificarse o consultarse.
  • No es reductivo. No digo que sea bueno ni malo, es una consecuencia de ser un Proxy y hay que tenerla en cuenta.

Cómo funciona la reactividad más ligera

La función recibe el objeto original cuyas propiedades y cuyos métodos seguirán accesibles a través del Proxy.

En ese objeto introduce un array done almacenará las funciones que ejecutar cuando cambie el valor de alguna propiedad e introduce el método que permite agregar esas funciones escuchadoras de cambios.

Lo que premite el Proxy es interceptar cuándo se asigna un valor a una propiedad mediante la sintaxis natural de javascript. En ese momento recorre todos los escuchadores y les notifica del cambio.

La razón de utilizar IdleCallback es evitar que varias contínuas asignaciones de valores lancen varias notificaciones entorpenciendo la ejecución del hilo principal.

function genSignal(obj) {
	let icb = null;
	obj.listeners = [];
	obj.listener = (listener, props = []) => {
		obj.listeners.push({ listener, props});
	};
	return new Proxy(obj, {
		set(obj, prop, nval) {
			Reflect.set(...arguments);
			const cval = obj[prop];
			icb && cancelIdleCallback(icb);
			icb = requestIdleCallback(() => {
				obj.listeners.map(data => {
					if(data.props.length===0 || data.props.includes(prop)) {
						data.listener(prop, cval, nval)
					}
				});
				icb = null;
			});
			return true;
		},
	});
}

Ejemplo

En un primer vistazo puede parece que ahorra poco esfuerzo, pero también ahorra muchas dependencias y respecto a soluciones convencionales (no-reactivas) evita tener que lanzar y escuchar CustomEvents para aplicar cambios en pantalla, lo cual puede terminar siendo poco mantenible.

  • Tienes que crear una variable que referencie a los inputs que te interesan (en otro caso el cambio podría venir de un fetch.
  • Tienes que actualizar el valor de la señal manualment (eso también sucede en React/Preact solo que jsx te permite hacerlo in-situ más cómodamente).
  • Tienes que actualizar el HTML manualmente (claro, porque estamos evitando el JSX) y eso lo haces mediante los listeners que son el equivalente a los useEffect de React/Preact.
GitHubThe most lightweight ReactivenessUna tabla de multiplicar configurable

Resumen

Ni digo que sea una maravilla, ni espero ordas de seguidores.

Digo que funciona y que a veces no hace falta más.

Digo que es interesante aprender otras tecnologías y a veces conviene tomar ideas prestadas y adaptarlas a tus necesidades puntuales.

Realmente, lo que haría React es ejecutar la función que modifica el famoso VirtualDOM con todo lo que implica. Por el contrario lo que haces aquí es modificar lo estrictamente necesario del DOM.


¹ Si no sabes qué es, básicamente es un disco virtual dentro de un navegador. Más info en MDN Origin Private File System.