NgRx Signal Store vs NgRx classico nel 2026: quale scegliere per il tuo progetto Angular?
Confronto tra NgRx Signal Store e NgRx classico per la gestione dello stato Angular. Funzionalità NgRx 22, esempi di codice e raccomandazioni.

NgRx Signal Store rappresenta un cambiamento fondamentale nella gestione dello stato Angular, sostituendo il tradizionale pattern ispirato a Redux basato su actions, reducers e selectors con un approccio reattivo basato sui signals. Con NgRx 22 (rilasciato ad agosto 2026), Signal Store ottiene resource extensions, dynamic deep signals e composabilità migliorata. Questo confronto esamina entrambe le soluzioni per aiutare gli sviluppatori Angular a prendere decisioni architetturali informate.
Utilizzare Signal Store per la gestione dello stato a livello di feature, applicazioni di media complessità e integrazione con la Resource API nativa di Angular. Utilizzare NgRx classico per applicazioni enterprise che richiedono audit trail rigorosi, side effects complessi e debugging avanzato con DevTools.
Comprendere le differenze architetturali
NgRx classico segue il pattern Redux: le actions descrivono eventi, i reducers producono nuovo stato immutabile, e i selectors estraggono dati. Ogni cambiamento di stato passa attraverso una pipeline prevedibile e tracciabile.
Signal Store adotta un approccio diverso. Invece di inviare actions attraverso reducers, lo stato risiede in signals reattivi. Gli aggiornamenti avvengono tramite metodi che chiamano patchState(), e i valori derivati provengono da computed signals invece che da selectors memorizzati.
| Aspetto | NgRx classico | Signal Store |
|---|---|---|
| Modello mentale | Redux/Flux | Servizi reattivi |
| Boilerplate | Alto (actions, reducers, selectors, effects) | Basso (state, metodi, computed) |
| Reattività | RxJS Observables | Angular Signals |
| Resource API | Integrazione manuale | Nativa con @ngrx/signals/resource |
| DevTools | Redux DevTools completi | Signal Store DevTools (via plugin) |
| Curva di apprendimento | Ripida | Moderata |
| Dimensione bundle | Maggiore (~15-20KB gzipped) | Minore (~4KB gzipped) |
Per gli sviluppatori già familiari con Angular Signals, Signal Store risulta un'estensione naturale delle primitive reattive del framework.
NgRx classico: il pattern Redux in Angular
NgRx classico organizza la gestione dello stato attorno ad actions, reducers, selectors ed effects. Questa separazione garantisce prevedibilità a costo di maggiore verbosità.
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { Book } from './book.model';
export const BooksActions = createActionGroup({
source: 'Books',
events: {
'Load Books': emptyProps(),
'Load Books Success': props<{ books: Book[] }>(),
'Load Books Failure': props<{ error: string }>(),
'Add Book': props<{ book: Book }>(),
'Remove Book': props<{ id: string }>(),
},
});L'API createActionGroup raggruppa actions correlate sotto un'unica source, migliorando la leggibilità nei DevTools e riducendo il boilerplate rispetto alle chiamate individuali di createAction.
import { createReducer, on } from '@ngrx/store';
import { BooksActions } from './books.actions';
import { Book } from './book.model';
export interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const booksReducer = createReducer(
initialState,
on(BooksActions.loadBooks, (state) => ({
...state,
loading: true,
error: null,
})),
on(BooksActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false,
})),
on(BooksActions.loadBooksFailure, (state, { error }) => ({
...state,
loading: false,
error,
}))
);I reducers rimangono funzioni pure che restituiscono nuovi oggetti stato. La funzione on() gestisce il matching delle actions e gli aggiornamenti dello stato in modo conciso.
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { BooksState } from './books.reducer';
export const selectBooksState = createFeatureSelector<BooksState>('books');
export const selectAllBooks = createSelector(
selectBooksState,
(state) => state.books
);
export const selectBooksLoading = createSelector(
selectBooksState,
(state) => state.loading
);
export const selectPublishedBooks = createSelector(
selectAllBooks,
(books) => books.filter((book) => book.published)
);I selectors forniscono la memoizzazione automaticamente. Il selector selectPublishedBooks ricalcola solo quando l'array books cambia, rendendo le letture efficienti anche in store frequentemente aggiornati.
NgRx Signal Store: gestione dello stato signals-first
Signal Store consolida stato, valori computed e metodi in un unico servizio iniettabile. La documentazione ufficiale NgRx lo descrive come una "soluzione completa per la gestione dello stato con supporto nativo per Angular Signals".
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
import { BooksService } from './books.service';
import { Book } from './book.model';
interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const BooksStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withComputed(({ books }) => ({
publishedBooks: computed(() => books().filter((book) => book.published)),
totalBooks: computed(() => books().length),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, { loading: true, error: null });
try {
const books = await booksService.getAll();
patchState(store, { books, loading: false });
} catch (error) {
patchState(store, { loading: false, error: 'Failed to load books' });
}
},
addBook(book: Book) {
patchState(store, ({ books }) => ({ books: [...books, book] }));
},
removeBook(id: string) {
patchState(store, ({ books }) => ({
books: books.filter((book) => book.id !== id),
}));
},
}))
);L'intera gestione dello stato di una feature sta in un unico file. Le proprietà dello stato diventano automaticamente signals, i computed signals sostituiscono i selectors, e i metodi sostituiscono il dispatch delle actions.
NgRx 22: Resource Extensions e withProps
NgRx 22 introduce l'entry point @ngrx/signals/resource, che permette l'integrazione diretta con la Resource API di Angular. La feature withProps centralizza l'injection dei servizi, mentre le resource extensions gestiscono pattern asincroni comuni.
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withProps } from '@ngrx/signals';
import { extendResource, withPreviousValueOnLoading, withValueOnError } from '@ngrx/signals/resource';
import { httpResource } from '@angular/core';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksResourceStore = signalStore(
{ providedIn: 'root' },
// Iniettare servizi tramite withProps (privati per convenzione con underscore)
withProps(() => ({
_booksService: inject(BooksService),
})),
// Configurare resources con extensions
withProps((store) => ({
_booksResource: extendResource(
httpResource<Book[]>({
url: '/api/books',
defaultValue: [],
}),
withPreviousValueOnLoading(), // Mantiene l'ultimo valore durante il reload
withValueOnError([]) // Restituisce array vuoto in caso di errore
),
})),
// Esporre resource readonly ai consumer
withProps((store) => ({
booksResource: store._booksResource.asReadonly(),
})),
withComputed((store) => ({
books: computed(() => store.booksResource.value()),
isLoading: computed(() => store.booksResource.isLoading()),
publishedBooks: computed(() =>
store.booksResource.value().filter((book) => book.published)
),
}))
);Le resource extensions risolvono due problemi comuni: il flickering durante i reload (risolto da withPreviousValueOnLoading) e la gestione degli errori (risolta da withValueOnError o withPreviousValueOnError). Queste extensions sono componibili e possono essere fornite globalmente tramite provideResourceExtensions.
Gestione delle entità con withEntities
Entrambi gli approcci supportano le collezioni di entità, ma la feature withEntities di Signal Store riduce drasticamente il codice.
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods } from '@ngrx/signals';
import {
addEntity,
removeEntity,
setAllEntities,
updateEntity,
withEntities,
} from '@ngrx/signals/entities';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksEntityStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withComputed(({ entities }) => ({
publishedBooks: computed(() => entities().filter((book) => book.published)),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
const books = await booksService.getAll();
patchState(store, setAllEntities(books));
},
addBook(book: Book) {
patchState(store, addEntity(book));
},
updateBook(id: string, changes: Partial<Book>) {
patchState(store, updateEntity({ id, changes }));
},
removeBook(id: string) {
patchState(store, removeEntity(id));
},
}))
);La feature withEntities fornisce automaticamente i signals ids, entityMap e entities. Gli updater per entità come addEntity, removeEntity e updateEntity gestiscono le mutazioni della collezione.
Pronto a superare i tuoi colloqui su Angular?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Custom Store Features per la riusabilità
La funzione signalStoreFeature di Signal Store permette di estrarre logiche trasversali e riutilizzarle tra diversi store. NgRx 22 aggiunge SignalStoreFeatureType per semplificare l'estrazione dei tipi.
import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState, SignalStoreFeatureType } from '@ngrx/signals';
type RequestStatus = 'idle' | 'pending' | 'fulfilled' | 'error';
export function withRequestStatus() {
return signalStoreFeature(
withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
withComputed(({ requestStatus }) => ({
isPending: computed(() => requestStatus() === 'pending'),
isFulfilled: computed(() => requestStatus() === 'fulfilled'),
isError: computed(() => requestStatus() === 'error'),
}))
);
}
// NgRx 22: estrarre tipi senza ridichiarazione manuale
export type RequestStatusFeature = SignalStoreFeatureType<typeof withRequestStatus>;
export function setPending(): { requestStatus: RequestStatus } {
return { requestStatus: 'pending' };
}
export function setFulfilled(): { requestStatus: RequestStatus } {
return { requestStatus: 'fulfilled' };
}
export function setError(): { requestStatus: RequestStatus } {
return { requestStatus: 'error' };
}Questa feature custom può ora essere composta in qualsiasi store che necessita del tracciamento dello stato di caricamento.
import { inject } from '@angular/core';
import { patchState, signalStore, withMethods } from '@ngrx/signals';
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
import { withRequestStatus, setFulfilled, setPending, setError } from './with-request-status';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withRequestStatus(),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, setPending());
try {
const books = await booksService.getAll();
patchState(store, setAllEntities(books), setFulfilled());
} catch {
patchState(store, setError());
}
},
}))
);Lo store composto ora espone i computed signals isPending(), isFulfilled() e isError() senza duplicare la logica.
Tracciamento sincrono dello stato con watchState
NgRx 22 aggiunge la funzione watchState per il tracciamento sincrono delle modifiche di stato, utile per funzionalità di undo/redo o cronologia dello stato.
import { inject, DestroyRef } from '@angular/core';
import { watchState } from '@ngrx/signals';
import { BooksStore } from './books.store';
export function setupStateHistory() {
const store = inject(BooksStore);
const destroyRef = inject(DestroyRef);
const history: unknown[] = [];
// Tracciamento sincrono: cattura ogni modifica di stato, nessun coalescing
watchState(store, (state) => {
history.push(structuredClone(state));
});
return { history };
}A differenza del tracciamento basato su effect che raggruppa gli aggiornamenti, watchState si attiva in modo sincrono ad ogni chiamata patchState, catturando la cronologia completa dello stato.
Pattern di integrazione nei componenti
Signal Store si integra naturalmente con la sintassi dei template Angular attraverso la lettura dei signals.
import { Component, inject } from '@angular/core';
import { BooksStore } from './books.store';
@Component({
selector: 'app-books-list',
standalone: true,
template: `
@if (store.isPending()) {
<div class="loading-spinner">Loading books...</div>
}
@if (store.isError()) {
<div class="error-message">Failed to load books</div>
}
@for (book of store.entities(); track book.id) {
<div class="book-card">
<h3>{{ book.title }}</h3>
<p>{{ book.author }}</p>
<button (click)="store.removeBook(book.id)">Remove</button>
</div>
}
<p>Total: {{ store.totalBooks() }} books</p>
`,
})
export class BooksListComponent {
readonly store = inject(BooksStore);
constructor() {
this.store.loadBooks();
}
}Nessuna gestione delle subscription necessaria. Nessun async pipe. I signals si integrano con la change detection zoneless di Angular per prestazioni ottimali. Con OnPush come strategia di change detection predefinita in Angular 22, i componenti Signal Store beneficiano di aggiornamenti precisi e granulari.
Performance e dimensione del bundle
L'architettura più leggera di Signal Store si traduce in bundle più piccoli. Il package core @ngrx/signals aggiunge circa 4KB gzipped, rispetto ai 15-20KB dello stack NgRx classico completo (@ngrx/store, @ngrx/effects, @ngrx/entity).
Signal Store beneficia inoltre della change detection basata sui signals di Angular. I computed signals ricalcolano solo quando le dipendenze cambiano, e i componenti ri-renderizzano solo quando i signals consumati si aggiornano. Questa reattività granulare elimina cicli di change detection non necessari comuni nei pattern basati su Observable.
Per applicazioni che utilizzano già intensivamente RxJS, i selectors basati su Observable di NgRx classico si integrano perfettamente con le pipeline esistenti.
NgRx 22 richiede Angular 22.x, TypeScript 6.0.x e RxJS 6.5.x o 7.5.x. Aggiornare con ng update @ngrx/signals@22 per progetti Signal Store.
Strategia di migrazione da classico a Signal Store
Migrare un'applicazione NgRx classica esistente non richiede una riscrittura totale. Entrambe le soluzioni possono coesistere, permettendo un'adozione incrementale.
- Iniziare con nuove features usando Signal Store
- Migrare i moduli feature isolati uno alla volta
- Mantenere features complesse con molti effects su NgRx classico se il costo di migrazione supera i benefici
- Utilizzare le utility di interoperabilità NgRx Signals per collegare stato basato su Observable e basato su signals
NgRx Store classico e Signal Store possono funzionare insieme nella stessa applicazione. Il team NgRx supporta ufficialmente entrambi gli approcci e fornisce utility di interoperabilità per collegarli.
Quando scegliere ciascun approccio
Scegliere Signal Store quando:
- Si sviluppano nuove applicazioni Angular 22+
- Si gestisce stato a livello di feature o componente
- Si integra con la Resource API e Signal Forms di Angular
- La developer experience e la riduzione del boilerplate sono priorità
- Il team ha esperienza limitata con NgRx
- La dimensione del bundle è importante
Scegliere NgRx classico quando:
- Si mantengono applicazioni NgRx esistenti
- Sono richiesti logging rigoroso delle actions e audit trail
- Si costruiscono workflow complessi con side effects estesi
- È necessario il time-travel debugging completo con Redux DevTools
- Il team possiede profonda esperienza NgRx
Per i fondamenti della gestione dello stato, comprendere entrambi gli approcci prepara gli sviluppatori per codebase Angular diverse.
Fonti
- NgRx v22 Release Announcement - Resource extensions, dynamic deep signals, SignalStoreFeatureType
- Angular 22 Feature Overview - Signal Forms stable, OnPush default, Resource API
- Using Angular's Resource API with NgRx Signal Store - withProps patterns, httpResource integration
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Signal Store vs NgRx classico: punti chiave
- Signal Store riduce il boilerplate della gestione dello stato del 60-70% rispetto a NgRx classico
- NgRx 22 aggiunge resource extensions (
withPreviousValueOnLoading,withValueOnError) per una gestione asincrona dello stato senza interruzioni - La feature
withPropscentralizza l'injection dei servizi, eliminando liste di parametri ripetute tra le features watchStatepermette il tracciamento sincrono dello stato per undo/redo e cronologia dello stato- NgRx classico rimane la scelta migliore per applicazioni enterprise che richiedono audit trail rigorosi e side effects complessi
- Entrambi gli approcci possono coesistere nella stessa applicazione, abilitando una migrazione incrementale
- Per nuovi progetti Angular 22+, Signal Store dovrebbe essere la scelta predefinita a meno che requisiti specifici non richiedano NgRx classico
Sapresti trovare il bug in Angular?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 9 ottobre 2026
Condividi
Articoli correlati

Lavoro freelance Angular developer 2026: dove trovare contratti, tariffe giornaliere e preparazione
Gli sviluppatori Angular freelance nel 2026 beneficiano di tariffe stabili e progetti enterprise. Panoramica su piattaforme, compensi e preparazione ai colloqui.

Lavoro come sviluppatore Angular nel 2026: dove cercare, stipendi e preparazione ai colloqui
Il mercato del lavoro per sviluppatori Angular a settembre 2026: offerte attive, statistiche sugli stipendi, opzioni remote e preparazione mirata ai colloqui.

Angular Signals e Computed nel 2026: Reattività Fine-Grained e Domande per Colloqui
Una guida approfondita ad Angular Signals, Computed Signals e al modello reattivo in Angular 20+. Include best practice, interoperabilità con RxJS e domande frequenti nei colloqui tecnici.