Reactividad y Manejo de Estado en Angular con NGRX
Resumen
El concepto de reactividad básica es muy importante en el desarrollo front-end. Se trata del estado de la aplicación con respecto al valor de los datos en cada componente, cómo estos cambian a medida que el usuario interactúa y cómo se actualiza la interfaz.
Problemas en la comunicación de componentes
Cuando pensamos en cómo comunicar un componente padre con su hijo y viceversa, solemos utilizar los decoradores @Input() y @Output().
Pero muchas veces, en aplicaciones grandes, la comunicación de componentes se vuelve mucho más compleja y estas herramientas no alcanzan cuando se necesita enviar información de un componente ""hijo"" a uno ""abuelo"".
Solución a la comunicación de componentes
Es recomendable implementar un patrón de diseño para mantener el estado de la aplicación centralizado en un único punto, para que todos los componentes accedan a ellos siempre que necesiten. A este punto central se lo conoce como Store.
{height="""" width=""""}
Implementando un store de datos
Los store de datos suelen implementarse haciendo uso de Observables.
Paso 1:
Importa la clase BehaviorSubject desde la librería RxJS, que te ayudará a crear una propiedad observable, a la cual tu componente pueda suscribirse y reaccionar ante ese cambio de estado.
// services/store.service.tsimport{BehaviorSubject}from'rxjs';@Injectable({providedIn:'root'})exportclassStoreService{privatemyShoppingCart:Producto[]=[];private myCart =newBehaviorSubject<Producto[]>([]);public myCart$ =this.myCart.asObservable();constructor(){}addProducto(producto:Producto):void{// El observable emitirá un nuevo valor con cada producto que se agregue al carrito.this.myShoppingCart.push(producto);this.myCart.next(this.myShoppingCart);}}
Paso 2: Suscribe a cualquier componente que necesites a estos datos, para reaccionar cuando estos cambian.
// components/nav-bar/nav-bar.component.tsimport{StoreService}from'src/app/services/store.service';import{Subscription}from'rxjs';@Component({selector:'app-nav-bar',templateUrl:'./nav-bar.component.html',styleUrls:['./nav-bar.component.scss']})exportclassNavBarComponentimplementsOnInit,OnDestroy{private sub$!:Subscription;constructor(privatestoreService:StoreService){}ngOnInit():void{this.storeService.myCart$.subscribe(data=>{// Cada vez que el observable emita un valor, se ejecutará este códigoconsole.log(data);});}ngOnDestroy():void{this.sub$.unsubscribe();}}
El lugar más apropiado para esto es en ngOnInit(). No olvides guardar este observable en una propiedad del tipo Subscription para hacer un unsubscribe() cuando el componente sea destruido.
NOTA: Por convención, las propiedades que guardan observables suelen tener un ""$"" al final del nombre para indicar que se trata de un observable.
Like si tambien te gustaria un curso dedicado a RXJS.
voto por uno de ngRx también
Rxjs y ngRx apoyo
Excelente video, me pareció simplemente genial como Nicolas explica la reactividad, además agradezco que lo hayan incluido ya que es un tema importante, me encantaría ver un curso completo acerca de reactividad para Angular.
Expresa tu apoyo con un me gusta si estas de acuerdo.
Un like si apoyan mi comentario de que haya un curso de Rxjs y ngRx.
Aquí hay que seguir aprendiendo de cada librería disponible.
Coincido con los comentarios, hace falta un curso especifico de Rxjs en Angular
Es un tema súper importante y merece un curso propio.
Tengo la duda de cómo evitar que el contador reinicie cuando el usuario refresca la pantalla? Que opciones hay? Es una buena practica usar LocalStorage?
Puedes utilizar los eventos del ciclo de vida de un componente para lo que indicas, segun sea el caso podrias utilizar un evento cuando el usuario carga componente (ngOnInit) u en donde lo prefieras incluso en un evento con una validacion personalizada.
Victor gracias por la respuesta. Los eventos de ciclo de vida los tengo claro, el problema sucede cuando el usuario actualiza la pantalla el contador se va a volver a cero por mas que guardes el valor en un servicio, simplemente porque al actualizar la app se reinicia, por lo tanto la instancia del servicio es otra.
Para evitar eso y que tome el valor que el contador tenia previo a actualizar la pantalla, es necesario de LocalStorage, lo que no se si es buena practica.
También todo mi apoyo para un curso de reactividad con RxJS y NGRx en Angular.
O NGXs
Algo super importante cuando existen subscripciones a observables que no son de http o de route (que por sí mismas se destruyen), es que debemos manejar su destrucción del componente. Esto porque puede generar un memory leak.
Yo creé un método que realizara la suscripción y la retornara para manejarla.
Y dentro del onInit() la almaceno en una variable o arreglo (en caso que deba manejar más de una subscripción a observables) de tipo Subscription. Ej:
ngOnInit():void{// Si se tendrán más subscripcionesthis.subscriptions=[this.getMyCartLength()]// Manejar una sola subscripciónthis.subscription=this.getMyCartLength()}
Todo esto, para que cuando el componente de desmonte, y se ejecute el método onDestroy() del ciclo de vida. También destruya las subscripciones que tiene activas.
ngOnDestroy():void{// Si se tendrán más subscripcionesthis.subscriptions.forEach(sub=>{if(sub !==null){ sub.unsubscribe()}});// Manejar una sola subscripciónthis.subscription?this.subscription.unsubscribe():null;}
Excelente aporte
como declararías el array de tipo subscriptions?
Seria super bueno que indicaran como se utlizan los test de cada proceso
Quiero un curso de RXJS para Angular
Programación Reactiva
La programación reactiva sigue el patrón de diseño Observer; cuando hay un cambio de estado en un objeto, los otros objetos son notificados y actualizados acorde.
apoyo la idea de que se hagan una serie de cursos sobre rxjs y reactividad en angular
Me sumo, yo también apoyo esa propuesta. Vamos platzi!
Muy buena explicación. El manejo de estado mediante un servicio nos quita de varias cosas como la comunicación que tendríamos que desarrollar para hacerlo por medio de inputs y outputs. Me gustaría saber su opinión de NGRX, aún se sigue usando o realmente hay mejores herramientas para mejorar el estado?
Tengo una gran pregunta, en foros he visto que al utilizar asObservable elimina la implementación del Observador(Observer). Por lo tanto, no puede llamar a next, error & complete en la instancia devuelta por asObservable().
que en typescriipt seria mejor hacer esto:
Tengo problemas al usar .next() porque me aparece que ese método no existe en el observable así que trate de hacerlo como dices pero sigue sin funcionar.
En mi código tengo algo como esto
private cart =newBehaviorSubject<Product[]>([]);// Agregar producto al carritoaddtoCart(product:Product){this.shoppingCart.push(product);// Transmitir el estadothis.cart$.next(...)}
te falta el atributo myCart$ = this.cart.asObservable();
Nice!
hola, me puede explicar algo, no entiendo la sintaxis del parametro que estas enviando en el metodo next donde envias
[... this.myCartState.value, product]. Gracias
Quiero utilizar la reactividad en mi código .
En mis servicios tengo un post a una API, ¿Cómo implemento el BehabiorSubject en este caso?
Hola, si bien puedes seguir usando el BehaviorSubject sin problema con rxjs también más adelante en este curso vemos como trabajar con las signals directamente en los servicios, ahora si quieres unir rxjs + signls Angular tiene un paquete para convertir de signal a observable y viceversa.