# NgRx Signal Store vs NgRx Classique en 2026 : Comment Choisir ? > Comparaison approfondie entre NgRx Signal Store et NgRx classique pour la gestion d'état Angular en 2026. Découvrez les différences architecturales, exemples de code et conseils pour choisir la bonne solution. - Published: 2026-07-11 - Updated: 2026-07-11 - Author: SharpSkill - Reading time: 5 min --- NgRx Signal Store représente un changement fondamental dans la gestion d'état Angular, remplaçant le pattern traditionnel Redux (actions-reducers-selectors) par une approche réactive basée sur les signaux. Cette comparaison examine les deux solutions pour aider les développeurs Angular à prendre des décisions architecturales éclairées en 2026. > **Guide de Décision Rapide** > > Utiliser **Signal Store** pour la gestion d'état au niveau fonctionnalité et les applications de complexité moyenne où la réduction du boilerplate est importante. Utiliser **NgRx Classique** pour les applications d'entreprise nécessitant des pistes d'audit strictes, des effets de bord complexes et un débogage extensif avec DevTools. ## Comprendre les Différences Architecturales NgRx classique suit le [pattern Redux](https://redux.js.org/understanding/thinking-in-redux/three-principles) : les actions décrivent les événements, les reducers produisent un nouvel état immuable, et les selectors extraient les données. Chaque changement d'état passe par un pipeline prévisible et traçable. Signal Store adopte une approche différente. Au lieu de dispatcher des actions via des reducers, l'état réside dans des signaux réactifs. Les mises à jour s'effectuent via des méthodes qui appellent `patchState()`, et les valeurs dérivées proviennent de signaux computed plutôt que de selectors mémorisés. | Aspect | NgRx Classique | Signal Store | |--------|----------------|---------------| | Modèle Mental | Redux/Flux | Services Réactifs | | Boilerplate | Élevé (actions, reducers, selectors, effects) | Faible (state, methods, computed) | | Réactivité | Observables RxJS | Signaux Angular | | DevTools | Redux DevTools complet | Signal Store DevTools (via plugin) | | Courbe d'apprentissage | Raide | Modérée | | Taille du Bundle | Plus grande | Plus petite | Pour les développeurs déjà familiers avec les [Signaux Angular](/technologies/angular/interview-questions/angular-signals), Signal Store apparaît comme une extension naturelle des primitives réactives du framework. ## NgRx Classique : Le Pattern Redux dans Angular NgRx classique organise la gestion d'état autour des actions, reducers, selectors et effects. Cette séparation impose la prévisibilité au prix de la verbosité. ```typescript // books.actions.ts 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` regroupe les actions liées sous une source unique, améliorant la lisibilité dans DevTools et réduisant le boilerplate par rapport aux appels individuels `createAction`. ```typescript // books.reducer.ts 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, })) ); ``` Les reducers restent des fonctions pures qui retournent de nouveaux objets d'état. La fonction `on()` gère la correspondance des actions et les mises à jour d'état de manière concise. ```typescript // books.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { BooksState } from './books.reducer'; export const selectBooksState = createFeatureSelector('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) ); ``` Les selectors fournissent la mémoïsation automatiquement. Le selector `selectPublishedBooks` ne recalcule que lorsque le tableau de livres change, rendant les lectures efficaces même dans des stores fréquemment mis à jour. ## NgRx Signal Store : La Gestion d'État Signaux-First Signal Store consolide l'état, les valeurs computed et les méthodes dans un service injectable unique. La [documentation officielle NgRx](https://ngrx.io/guide/signals/signal-store) le décrit comme une "solution de gestion d'état complète avec support natif des Signaux Angular." ```typescript // books.store.ts 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'ensemble de la gestion d'état d'une fonctionnalité tient dans un seul fichier. Les propriétés d'état deviennent automatiquement des signaux, les signaux computed remplacent les selectors, et les méthodes remplacent les dispatches d'actions. ## Gestion des Entités avec withEntities Les deux approches supportent les collections d'entités, mais la fonctionnalité `withEntities` de Signal Store réduit drastiquement le code. ```typescript // books-entity.store.ts 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(), 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) { patchState(store, updateEntity({ id, changes })); }, removeBook(id: string) { patchState(store, removeEntity(id)); }, })) ); ``` La fonctionnalité `withEntities` fournit automatiquement les signaux `ids`, `entityMap` et `entities`. Les updaters d'entités comme `addEntity`, `removeEntity` et `updateEntity` gèrent les mutations de collections. ## Features de Store Personnalisées pour la Réutilisabilité La fonction `signalStoreFeature` de Signal Store permet d'extraire et de réutiliser des préoccupations transversales à travers les stores. ```typescript // with-request-status.ts import { computed } from '@angular/core'; import { signalStoreFeature, withComputed, withState } 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'), })) ); } export function setPending(): { requestStatus: RequestStatus } { return { requestStatus: 'pending' }; } export function setFulfilled(): { requestStatus: RequestStatus } { return { requestStatus: 'fulfilled' }; } export function setError(): { requestStatus: RequestStatus } { return { requestStatus: 'error' }; } ``` Cette feature personnalisée peut maintenant être composée dans n'importe quel store nécessitant le suivi de l'état de chargement. ```typescript // books-with-status.store.ts 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(), 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()); } }, })) ); ``` Le store composé expose maintenant les signaux computed `isPending()`, `isFulfilled()` et `isError()` sans dupliquer la logique. ## Patterns d'Intégration dans les Composants Signal Store s'intègre naturellement avec la syntaxe des templates Angular via la lecture des signaux. ```typescript // books-list.component.ts import { Component, inject } from '@angular/core'; import { BooksStore } from './books.store'; @Component({ selector: 'app-books-list', standalone: true, template: ` @if (store.isPending()) {
Chargement des livres...
} @if (store.isError()) {
Échec du chargement des livres
} @for (book of store.entities(); track book.id) {

{{ book.title }}

{{ book.author }}

}

Total : {{ store.totalBooks() }} livres

`, }) export class BooksListComponent { readonly store = inject(BooksStore); constructor() { this.store.loadBooks(); } } ``` Pas de gestion des souscriptions. Pas de pipes async. Les signaux s'intègrent avec la [détection de changement zoneless](/blog/angular/angular-19-zoneless-change-detection-performance) d'Angular pour des performances optimales. ## Considérations de Performance et Taille de Bundle L'architecture plus légère de Signal Store se traduit par des bundles plus petits. Le package core `@ngrx/signals` ajoute environ 4KB gzippé, comparé à 15-20KB pour la stack NgRx classique complète (`@ngrx/store`, `@ngrx/effects`, `@ngrx/entity`). Signal Store bénéficie également de la détection de changement basée sur les signaux d'Angular. Les signaux computed ne recalculent que lorsque les dépendances changent, et les composants ne se re-rendent que lorsque les signaux consommés sont mis à jour. Cette réactivité granulaire élimine les cycles de détection de changement inutiles courants dans les patterns basés sur les Observables. Pour les applications utilisant déjà [RxJS intensivement](/blog/angular/rxjs-angular-operators-subjects-signals-interop), les selectors basés sur les Observables de NgRx classique s'intègrent parfaitement avec les pipelines existants. ## Stratégie de Migration de Classique vers Signal Store Migrer une application NgRx classique existante ne nécessite pas une réécriture complète. Les deux solutions peuvent coexister, permettant une adoption incrémentale. - Commencer par les nouvelles fonctionnalités avec Signal Store - Migrer les modules de fonctionnalités isolés un par un - Conserver les fonctionnalités complexes avec beaucoup d'effects sur NgRx classique si le coût de migration dépasse le bénéfice - Utiliser les utilitaires [d'interopérabilité NgRx Signals](https://ngrx.io/guide/signals) pour créer des ponts entre l'état basé sur les Observables et celui basé sur les signaux > **La Coexistence Fonctionne** > > NgRx Store classique et Signal Store peuvent fonctionner côte à côte dans la même application. L'équipe NgRx supporte officiellement les deux approches et fournit des utilitaires d'interopérabilité pour créer des ponts entre elles. ## Quand Choisir Chaque Approche ### Choisir Signal Store quand : - Construction de nouvelles applications Angular 20+ - Gestion de l'état au niveau fonctionnalité ou composant - Priorité à l'expérience développeur et à la réduction du boilerplate - L'équipe a une expérience NgRx limitée - La taille du bundle est importante ### Choisir NgRx Classique quand : - Maintenance d'applications NgRx existantes - Besoin de pistes d'audit strictes avec logging des actions - Construction de workflows complexes avec des effets de bord extensifs - Besoin du débogage time-travel complet de Redux DevTools - L'équipe possède une expertise NgRx approfondie Pour les [fondamentaux de la gestion d'état](/technologies/angular/interview-questions/state-management-basics), comprendre les deux approches prépare les développeurs à divers codebases Angular. ## Conclusion - Signal Store réduit le boilerplate de gestion d'état de 60 à 70% par rapport à NgRx classique - NgRx classique reste le meilleur choix pour les applications d'entreprise nécessitant des pistes d'audit strictes et des effets de bord complexes - Les deux approches peuvent coexister dans la même application, permettant une migration incrémentale - Les signaux computed de Signal Store fournissent une mémoïsation automatique sans définitions explicites de selectors - Les features de store personnalisées (`signalStoreFeature`) permettent des préoccupations transversales réutilisables - Pour les nouveaux projets Angular 20+ en 2026, Signal Store devrait être le choix par défaut sauf si des exigences spécifiques demandent NgRx classique --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/angular/ngrx-signal-store-vs-classic-ngrx-comparison