S

super-dev — full-stack

@super-dev.app · Full-stack · ★ disponible para proyectos
12,8k suscriptores·47 vídeos·Activo desde 2017

Desarrollador full-stack .NET / Angular con perfil DevOps en Azure Cloud. También cómodo con Flutter + Firebase. Construyo productos, los despliego, los monitorizo.

Volver a los artículos
ANGULAR
ANGULAR

Estado de la aplicación con NgRx SignalStore

Artículo 4 de 4 — Angular 21 en la práctica
Zoneless, signals, resource(), @defer, SignalStore — el frontend moderno, de verdad.

No todo el estado de una app cabe en un signal() perdido en el fondo de un componente. En cuanto un estado se comparte, se deriva y se muta desde varios lugares, se quiere una frontera clara: selectores de solo lectura, métodos para hacerlo evolucionar. NgRx SignalStore ( @ngrx/signals ) ofrece exactamente eso, sin el boilerplate de las actions/reducers del NgRx clásico.

Anatomía de un signalStore

Un store se compone de features encadenadas. withState declara el estado inicial, withComputed los valores derivados, withMethods las operaciones. Cada campo de estado se convierte automáticamente en un signal expuesto en la instancia.

TypeScript
1import { signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
2import { computed } from '@angular/core';
3
4export const CartStore = signalStore(
5 { providedIn: 'root' },
6 withState<{ items: CartItem[] }>({ items: [] }),
7 withComputed(({ items }) => ({
8 total: computed(() => items().reduce((sum, item) => sum + item.price * item.quantity, 0)),
9 count: computed(() => items().length),
10 })),
11 withMethods((store) => ({
12 add(item: CartItem): void {
13 patchState(store, { items: [...store.items(), item] });
14 },
15 clear(): void {
16 patchState(store, { items: [] });
17 },
18 })),
19);

El estado nunca se muta directamente: se pasa por patchState , que aplica una actualización inmutable y notifica a los signals implicados.

Selectores que son signals

store.total y store.count no son funciones que se llaman en el servicio: son computed , es decir, signals de pleno derecho. En un componente, se leen como cualquier signal, y el change detection zoneless solo re-renderiza lo que depende de ellos.

TypeScript
1export class CartBadge {
2 private readonly store = inject(CartStore);
3
4 protected readonly count = this.store.count;
5 protected readonly total = this.store.total;
6
7 protected checkout(): void {
8 this.store.clear();
9 }
10}

Componer con llamadas async

withMethods puede integrar rxMethod (desde @ngrx/signals/rxjs-interop ) para conectar un flujo RxJS, o simplemente async / await para un fetch . Se mantiene la lógica de orquestación en el store, el componente permanece como una vista. También es aquí donde se añade un estado loading para un patrón stale-while-revalidate.

¿Store o simple signal?

No todo necesita un store. Un estado local a un componente — una pestaña activa, la apertura de un menú — sigue siendo un signal() privado: un store añadiría indirección innecesaria. El SignalStore se justifica cuando el estado es:

  • compartido entre varios componentes o rutas;
  • derivado por varios computed que se quieren centralizar;
  • mutado por operaciones que se quieren probar en aislamiento.

La regla práctica: empieza con signals locales, extrae un store el día en que copies el mismo estado en un segundo componente. La documentación cubre cada feature en la guía SignalStore .

El SignalStore no es el NgRx «actions en todas partes» de ayer. Es una fachada de signals: solo lectura en salida, métodos en entrada, cero reducers. Mantienes la disciplina de un store sin pagar su ceremonial.
super-dev — portfolio.app
// Continuar en ANGULAR
Un motor estilo DOOM en el navegador, con tres backends
11 min • 1,4k lecturas
Migrar una app Angular a zoneless + signals
8 min • 4,1k lecturas
Cargar datos con resource() y httpResource()
7 min • 3,6k lecturas