CodeForge

JavaScript de Cero a Experto / Patrones y observables

El store observable: un valor que avisa

Teoría20 min20 XP

El bus transmite sucesos que vuelan; pero el corazón de una app es su ESTADO: el total, el usuario, el tema — valores que están ahí y cambian. Hoy construyes el patrón que los gobierna: el store observable — un valor con suscriptores. Son 15 líneas, y son el abuelo directo de los signals de Angular, de Zustand y del estado de React.

El marcador del estadio

Las 15 líneas

Tres decisiones de diseño para masticar: el set IGNORA cambios que no cambian nada (nuevo === valor) — sin eso, cada asignación redundante dispararía avisos en cascada; la suscripción avisa DE INMEDIATO con el valor actual — así el recién llegado no espera al próximo cambio para pintarse (fíjate: la pantalla imprimió "total: 0" al suscribirse); y el valor vive encerrado en la mochila — la ÚNICA puerta de escritura es set, tu garantía de que ningún rincón de la app cambia el estado sin que los demás se enteren.

Derivados: valores que se calculan solos

¿Y si un valor DEPENDE de otro? Se suscribe y se recalcula — el getter del módulo 11, en versión reactiva:

derivado.js
function derivar(estadoFuente, transformar) {
const derivado = crearEstado(transformar(estadoFuente.get()));
// cuando la fuente cambie, el derivado se recalcula y AVISA a los suyos
estadoFuente.suscribir((v) => derivado.set(transformar(v)));
return derivado;
}

const total = crearEstado(0);
const enCOP = derivar(total, (t) => t.toLocaleString("es-CO"));

enCOP.suscribir((texto) => console.log("formateado:", texto));
total.set(48000);   // "formateado: 48.000" — en cadena, solo

Cambias total y enCOP se actualiza y avisa a los suyos — una cadena reactiva. Y aquí la confesión que hace clic con todo: esto ES un signal. Cuando en Angular escribas total = signal(0) y enCOP = computed(...), o en React veas el estado re-renderizar componentes, estarás usando ESTE patrón con optimizaciones industriales (detección automática de dependencias, agrupación de avisos, integración con el render). El mecanismo — un valor, un solo escritor, suscriptores avisados — ya es tuyo.

Nota honesta: nuestro store guarda un VALOR simple y compara con === — suficiente aquí. Con objetos y arrays, el === compara referencias… y ahí la inmutabilidad del módulo 12 deja de ser filosofía y se vuelve REQUISITO: solo produciendo un objeto nuevo en cada cambio el store puede detectar que algo cambió. Por eso React exige inmutabilidad: su store compara referencias, exactamente como el tuyo.

Un compañero exporta el objeto de estado crudo y en un archivo lejano alguien hace estado.valor = 99999 directo (imagina que fuera posible). Los suscriptores no se enteran y la pantalla queda vieja. ¿Cuál es la lección de diseño?

Mini-reto

Sube tu store de nivel: 1) haz que set acepte también una FUNCIÓN (total.set((v) => v + 18000)) — si recibe función, la aplica al valor actual (inspecciona con typeof); 2) crea gastos = crearEstado([]) y súmale gastos con set-función + spread inmutable (¡módulo 12!); 3) deriva cantidad de gastos con tu derivar y comprueba la cadena completa: un push inmutable → dos avisos en cascada. Acabas de escribir la mitad de Zustand.

Qué sigue

Eventos que vuelan (bus), valores que avisan (store)… falta gobernar los PROCESOS: cosas que pasan por etapas — una petición que va de "cargando" a "éxito" o "error", un pedido que se paga y se envía. Cuando las etapas y sus caminos válidos se declaran explícitos, los bugs de "¿cómo llegó a este estado?" se extinguen. Próxima lección: la máquina de estados.