Índice de contenidos
- Introducción
- ¿Cómo funciona la detección de cambios ahora mismo en Angular?
- ¿Qué son los Signals?
- ¿Cómo nos ayudan los Signals en todo esto?
- ¿Cómo se crea un Signal?
- Pero, ¿y esto nos es lo mismo que un BehaviorSubject?
- Conclusión
Introducción
Mi intención con este tutorial es hacer una pequeña introducción a los Signals y darte el conocimiento básico sobre este nuevo primitivo reactivo que ha lanzado Angular en la versión 16.
¿Cómo funciona la detección de cambios ahora mismo en Angular?
Hasta ahora la detección de cambios automática en Angular se hacía mediante ZoneJs.
ZoneJs es una librería que proporciona zonas que son capaces de capturar actividades dentro de nuestra aplicación, es decir, cuando se produce una cambio en nuestra aplicación (una operación asíncrona, un evento del dom, etc.) es detectado por ZoneJs y este activa la detección de cambios de Angular, es entonces cuando Angular recorre todos los componentes del árbol de componentes para verificar si algún estado de algún componente ha cambiado o no y si este cambio afecta a la vista, en caso de ser así, actualiza la parte del DOM implicada.
Esto significa que incluso si no tenemos la intención de actualizar el DOM, Angular deberá recorrer todo el árbol de componentes para comprobar si algún dato ha cambiado y debe actualizar la parte correspondiente.
Existen distintas herramientas en Angular, como la estrategia de detección de cambios onPush o el AsyncPipe, para mejorar y simplificar todo este mecanismo de detección de cambios. Pero esto también requiere de cierta atención.
¿Qué son los Signals?
Pero… ¿Qué son los Signals? Pues la descripción que da Solid un framework relativamente nuevo pionero en el uso de señales y que se basa principalmente en ellas, me parece que lo explica bastante bien.
Signals son la piedra angular de la reactividad en Solid. Contienen valores que cambian con el tiempo; cuando cambia el valor de una señal, automáticamente actualiza todo lo que la usa.
Los Signals permiten la creación de relaciones reactivas entre datos, esto quiere decir que cuando un valor cambia, los valores que dependen de él también son notificados y se actualizan automáticamente.
Técnicamente, los Signals son funciones de argumento cero [( ()⇒ T)]
, cuando se ejecutan, simplemente devuelven un valor.
¿Cómo nos ayudan los Signals en todo esto?
Los Signals nos pueden ayudar a evitar todas esas comprobaciones innecesarias y simplemente actualizar la parte de la aplicación que ha cambiado, a esto se le conoce como reactividad de grano fino. Esto aumenta enormemente el rendimiento de la aplicación y permite una mayor velocidad de actualización en la interfaz de usuario.
Otro dato importante es que los Signals también permite la detección de cambios asíncronos, lo que significa que los cambios realizados por eventos que ocurren fuera del ciclo de vida normal de Angular también se pueden detectar y actualizar en la interfaz de usuario de manera eficiente.
¿Cómo se crea un Signal?
Creamos e inicializamos un Signal con la función signal()
foo = signal('foo')
La funcion signal()
devuelve una WritableSignal
. Signal es una función getter, pero el tipo WritableSignal
nos da la posibilidad de modificar su valor con el método set
, update
y mutate
.
set
: Da un nuevo valor al signal y notifica a sus consumidores.foo.set(’new value’)>
update
: Actualiza el valor del signal basado en su valor actual y notifica a sus consumidores.counter.update(number => number + 1);
mutate
: Se utiliza para modificar el contenido de un valor del signal, sin sustituir el propio valor del signal.obj = signal({ name: 'name' }); obj.mutate((v) => v.name = 'newName'); array = signal([]) array.mutate(list => list.push(newValue));
Pero la verdadera magia de los Signals es que son valores reactivos que notifican a sus consumidores cuando se produce un cambio en ellos.
Los consumidores son cualquier código que muestre interés en el valor del Signal y quiera ser notificado cuando este cambie.
Un consumidor puede ser simplemente la llamada al signal desde el template de un componente.
<p>{{foo()}}</p>
Existen 2 funciones propias de los Signals que son usadas como consumidores, effect y computed.
effect
: La funcióneffect()
es ejecutada cada vez que el valor de los Signals de los que sea consumidor cambie (si usas React puede que te suene al useEffect).effect(()=> console.log(`The value of ${foo()} has changed`));
Este mensaje se imprimirá todas las veces que foo
cambie.
computed
: La funcióncomputed()
crea una señal que es calculada a partir de una o más señales. El valor de la señal computada será recalculada cada vez que alguna de sus dependencias cambie.name = signal('Rubén'); surname = signal('Yáñez'); fullName = computed(()=> name() + surname()) console.log(fullName()) // Rubén Yáñez surname.set('Fernández') console.log(fullName()) // Rubén Fernández
Pero, ¿y esto nos es lo mismo que un BehaviorSubject?
En Angular ya teníamos un concepto similar llamado BehaviorSubject proveniente de la librería RxJS. Aunque ambas ideas poseen la misma finalidad que consiste en crear aplicaciones reactivas y ayudar a la gestión de estado de estas, se pueden ver varias diferencias, por ejemplo: Mientras que RxJS se basa en observables y patrones de programación reactiva, los Signals son una forma más simple y simplificada, los signals mejoran el rendimiento, ya que solo se realizan actualizaciones cuando son necesarias mientras que con RxJS, necesitas ser más cuidadoso con las subscripciones. Aun así Signals y RxJS pueden convivir perfectamente.
Conclusión
En resumen, el cambio de la reactividad de Angular a Signals representa una mejora significativa en la capacidad de detección de cambios y el rendimiento en comparación con la implementación anterior basada en ZoneJs. En mi opinión es un gran avance que nos facilita la reactividad en nuestras aplicaciones Angular.
Muchas gracias por haber leído este tutorial, espero que te haya sido útil ?