No hay nada nuevo que decir que son un gran fan de RxJS… que desde que lo descubri con angular para mi se volvio una de mis herramientas favoritas
al punto que 2 veces cree cursos gratuitos en youtube sobre esta tecnologia, aprovechando sus actualizaciones y novedades…
Pero angular, tomo un camino de decidir que tenia que cambiar algunas cosas en el framework, es un camino que viene de varias versiones para atras, y esto esta armando un poco de GRANDES cambios en el framework en si…
Por lo tanto, integraron Signals, con su reactividad primitiva, para utilizar en angular… esto hace que muchas cosas que soliamos hacer con rxjs u otros metodos, paso a utilizar signals, lo cual hace que mejore la performance debido a que el framework tiene que realizar menos chequeos y re-draws de algunas cosas en nuestros templates…
Por eso decidi hacer algunos videos y posts, los cuales van a ayudarte a entender esto, pero bien desde dentro… no solo a utilizarlos si no, a entender como funciona, porque deberiamos utilizarlos y cuando deberiamos utilizarlos.
Con esta intro, vamos a escribir un poco de codigo, en este post vamos a ver los principios de Signals, como funcionan que es un compute y los effects…
Podriamos ver los signals como un objeto que tiene las siguientes funciones:
- creacion con seteo de valor inicial
- getter
- setter
- update
estas cuatro funciones hacen de nuestro manejo de datos algo mas facil, y me preguntas porque…
Bueno, vamos a verlo en el codigo directamente y pasando a explicar cada uno de ellos.
El Signal se crea con un valor inicial SIEMPRE, depende de nosotros darle un type inicial o no, pero en el caso de que no lo hagamos el runtime lo va a hacer por nosotros y le va a setear el type del valor que le damos.
entonces, primero vamos a importarlo de @angular/core
, una vez que lo tenemos ya podemos usarlo
Como lo vamos a crear, bueno de una forma bastante simple,
version = signal(17);
en esta variable version, vamos a decirle que es del tipo signals y su generico va a pasar a ser un number.
QUE? COMO?
bueno, vamos a ver la firma del mismo
/**
* A `Signal` with a value that can be mutated via a setter interface.
*/
export declare interface WritableSignal<T> extends Signal<T> {
/**
* Directly set the signal to a new value, and notify any dependents.
*/
set(value: T): void;
/**
* Update the value of the signal based on its current value, and
* notify any dependents.
*/
update(updateFn: (value: T) => T): void;
/**
* Returns a readonly version of this signal. Readonly signals can be accessed to read their value
* but can't be changed using set, update or mutate methods. The readonly signals do _not_ have
* any built-in mechanism that would prevent deep-mutation of their value.
*/
asReadonly(): Signal<T>;
}
Aca esta la firma de los signals, usan generics, que es una de las grandes genialidades de typescript a la hora de crear cosas de este estilo.
Lo que vamos a indicarle es que ese generico (que dejara de serlo, va a ser el mismo type de esta instancia de signal), por lo tanto, siempre que lo utilicemos, ya sea para obtener el valor o modificarlo vamos a tener un number.
Sigamos, entonces, ahora tenemos la segunda función, el getter, que es basicamente obtener el valor un get.
Esto se hace con los parentesis directamente, sin la necesidad de escribir nada mas
version() en el template
o this.version() en el componente
ahora que pasa si nos olvidamos de esto y ponemos simplemente version
bueno…
nos vamos a encontrar la firma de la funcion, que es el getter() y su nombre en el prototipo.
Asi que si encuentran algo asi, ya sabes que se olvidaron los ()
parentesis.
Pero bueno sigamos con lo importante, obtener el dato, es facil, ahora que pasa si queremos actualizar el valor del mismo, bueno tal y como comente antes vamos a tener 2 opciones para hacerlo:
this.version.set(18);
el set
es la primera forma, que lo que va a hacer directamente es sobre-escribir el valor actual por el nuevo.
Ahora, muchas veces queremos actualizar el valor, pero utilizando el valor actual, por ejemplo en una cuenta (sumandole 1 por ejemplo).
Para eso tenemos update, que a diferencia de set, que sobre-escribe el valor, lo que hace es darnos el valor, para que podamos ejecutarlo en una funcion anonima la cual su retorno return
es el valor que va a “setear” en el signal.
this.version.update((valorActual) => valorActual + 1);
como asi podemos hacer algunas cosas un poco mas “complejas” que esta, por ejemplo
this.version.update( (valorActual) => {
if (valorActual === 10 && flag) {
return 11;
}
return valorActual + 1;
});
De esta forma, tenemos las actualizaciones y todas las funciones que podes obtener de los signals, pero hay una cosa que no te comente hasta ahora…
Hay 2 tipos de signals, los que acabamos de ver que son los WritableSignals
y luego tenemos los Signals
que a diferencia de los anteriores, no se pueden escribir/setear a “mano” por asi decirlos y van a necesitar de uno o mas signals para poder obtener valores y ejecutarse.
Estos son los computed
estos signals a diferencias de los anteriores, son funciones anonimas que son lazy y memorized, que quiere decir, que derivan del valor de otro signal, y dependen de su cambio de valor para actulizarse y ejecutarse
// writableSignal
version: WritableSignal<number> = signal(17); // return 17
// signal
versionComputed: Signal<number> = computed(() => {
return this.version() + 1;
}); // return 18
Aca esta las pequeñas diferencias, los computed, solo ejecutan una funcion anonima que va a ejecutarse cada vez que el/los signal/s que tiene dentro cambien de valor (que se les haga un set o update)… y entrega un nuevo valor.
Esta forma, los computed basicamente “reaccionan” a los cambios de otros signals, algo que estamos acotumbrados en cosas como el combineLatests de rxjs por ejemplo… que si alguno de los valores cambia se ejecuta de nuevo. Por esto mismo, se dice que sus dependencias son dinamicas, ya que depende 100% del cambio de valor de 1 o mas signals.
Y por ultimo tenemos, los effects, que es una funcion que se ejecuta siempre que un signal que este dentro de esta funcion, cambie de valor.
Los effects tienen bastantes complejidades si queremos verlos en profundidad, pero por ahroa lo vamos a ver bien por arriba, osea las funciones mas basicas que tiene:
effect(() => {
console.log('se modifico el valor de version: ', this.version());
});
Y asi se ven los effects, y esta funcion anonima que lleva dentro, solo se va a ejecutar cuando version
cambie de valor, en el caso de que nunca cambie, este effect nunca se ejecutara ya que es un efecto del cambio del signal.
El equipo de Angular, en su documentacion, habla de que los effects los deberiamos usar en por ejemplo:
- loggear datos cuando cambia para analitics o debbugear
- mantener los datos del localstorage en sync
- agregar custom data al DOM, que no se pueda hacer en el template directo
- para el elemento canvas para mejorar la performance del mismo.
Con esto, ya tenemos el primer vistaso de que son los signals, cuales son las funcionalidades que tienen y que podemos empezar a hacer con ellos, creo que tal vez lo mas importante que podemos ver es la diferencia entre un writableSignal y un signal comun, osea entre el signal que vamos a setear nosotros y el computed que es el que depende de otros.
En proximos capitulos, vamos a ver como funciona mas adentro los effects, como asi hacer una comparativa de funcionalidades y velocidad entre codigo con signals y codigo con rxjs.
Si quieren ver el codigo completo que utilice en el video lo tienen aqui: https://stackblitz.com/edit/stackblitz-starters-dcqlbf?file=src%2Fmain.ts
Se vienen cosas muy interesantes, asi que espero que esto te sirva mucho y estes muy atento a las novedades que voy a ir trayendo!
Gracias por llegar hasta aqui, nos vemos en la proxima!
—
JC
Top comments (2)
Falta el 2do artículo o yo no lo encuentro?
Excelente, aunque no es un tema a profundidad, he logrado comprender mucho mejor el uso de los signals y aún más, comprender el uso de computed que en realidad es lo que estaba buscando.