# NgRx Signal Store vs Класичний NgRx у 2026: Який Обрати? > Комплексне порівняння NgRx Signal Store та класичного NgRx для управління станом в Angular. Практичні приклади коду та рекомендації на 2026 рік. - Published: 2026-07-11 - Updated: 2026-07-11 - Author: SharpSkill - Tags: angular, ngrx, state-management, signals, typescript - Reading time: 9 min --- NgRx Signal Store представляє фундаментальний зсув в управлінні станом Angular, замінюючи традиційний патерн Redux (екшени-редюсери-селектори) реактивним підходом на основі сигналів. Це порівняння досліджує обидва рішення, щоб допомогти розробникам Angular приймати обґрунтовані архітектурні рішення у 2026 році. > **Швидкий посібник з прийняття рішень** > > Використовуйте **Signal Store** для стану на рівні фіч та застосунків середньої складності, де важливе зменшення бойлерплейту. Використовуйте **Класичний NgRx** для корпоративних застосунків, що вимагають суворих аудиторських слідів, складних побічних ефектів та розширеного налагодження в DevTools. ## Розуміння Архітектурних Відмінностей Класичний NgRx базується на [патерні Redux](https://redux.js.org/understanding/thinking-in-redux/three-principles): екшени описують події, редюсери створюють новий незмінний стан, а селектори витягують дані. Кожна зміна стану проходить через передбачуваний, відстежуваний конвеєр. Signal Store використовує інший підхід. Замість диспатчингу екшенів через редюсери, стан живе в реактивних сигналах. Оновлення відбуваються через методи, що викликають `patchState()`, а похідні значення отримуються з computed signals замість мемоїзованих селекторів. | Аспект | Класичний NgRx | Signal Store | |--------|---------------|---------------| | Ментальна модель | Redux/Flux | Реактивні сервіси | | Бойлерплейт | Високий (екшени, редюсери, селектори, ефекти) | Низький (стан, методи, computed) | | Реактивність | RxJS Observables | Angular Signals | | DevTools | Повні Redux DevTools | Signal Store DevTools (через плагін) | | Крива навчання | Крута | Помірна | | Розмір бандлу | Більший | Менший | Для розробників, вже знайомих з Angular Signals, Signal Store відчувається як природне розширення реактивних примітивів фреймворку. ## Класичний NgRx: Патерн Redux в Angular Класичний NgRx організовує управління станом навколо екшенів, редюсерів, селекторів та ефектів. Таке розділення забезпечує передбачуваність ціною багатослівності. ```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 }>(), }, }); ``` API `createActionGroup` групує пов'язані екшени під одним джерелом, покращуючи читабельність у DevTools та зменшуючи бойлерплейт порівняно з окремими викликами `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, })) ); ``` Редюсери залишаються чистими функціями, що повертають нові об'єкти стану. Функція `on()` лаконічно обробляє зіставлення екшенів та оновлення стану. ```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) ); ``` Селектори автоматично забезпечують мемоїзацію. Селектор `selectPublishedBooks` перераховується лише коли змінюється масив книг, роблячи читання ефективним навіть у store, що часто оновлюються. ## NgRx Signal Store: Управління Станом на Основі Сигналів Signal Store консолідує стан, computed значення та методи в єдиний ін'єктований сервіс. [Офіційна документація NgRx](https://ngrx.io/guide/signals/signal-store) описує його як "повнофункціональне рішення для управління станом з нативною підтримкою Angular Signals." ```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), })); }, })) ); ``` Все управління станом фічі вміщується в одному файлі. Властивості стану автоматично стають сигналами, computed signals замінюють селектори, а методи замінюють диспатчинг екшенів. ## Управління Сутностями з withEntities Обидва підходи підтримують колекції сутностей, але функція `withEntities` в Signal Store драматично зменшує кількість коду. ```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)); }, })) ); ``` Функція `withEntities` автоматично надає сигнали `ids`, `entityMap` та `entities`. Оновлювачі сутностей як `addEntity`, `removeEntity` та `updateEntity` обробляють мутації колекцій. ## Власні Фічі Store для Повторного Використання Функція `signalStoreFeature` в Signal Store дозволяє витягувати та повторно використовувати наскрізну функціональність у різних store. ```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' }; } ``` Цю власну фічу тепер можна скомпонувати в будь-який store, що потребує відстеження стану завантаження. ```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()); } }, })) ); ``` Скомпонований store тепер надає computed signals `isPending()`, `isFulfilled()` та `isError()` без дублювання логіки. ## Патерни Інтеграції з Компонентами Signal Store природно інтегрується з синтаксисом шаблонів Angular через читання сигналів. ```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()) {
Завантаження книг...
} @if (store.isError()) {
Не вдалося завантажити книги
} @for (book of store.entities(); track book.id) {

{{ book.title }}

{{ book.author }}

}

Всього: {{ store.totalBooks() }} книг

`, }) export class BooksListComponent { readonly store = inject(BooksStore); constructor() { this.store.loadBooks(); } } ``` Немає управління підписками. Немає async pipes. Сигнали інтегруються з zoneless change detection Angular для оптимальної продуктивності. ## Продуктивність та Розмір Бандлу Легша архітектура Signal Store перетворюється на менші бандли. Основний пакет `@ngrx/signals` додає приблизно 4KB gzip, порівняно з 15-20KB для повного стеку класичного NgRx (`@ngrx/store`, `@ngrx/effects`, `@ngrx/entity`). Signal Store також отримує вигоду від change detection Angular на основі сигналів. Computed signals перераховуються лише коли змінюються залежності, а компоненти перерендерюються лише коли оновлюються споживані сигнали. Ця гранулярна реактивність усуває непотрібні цикли change detection, типові для патернів на основі Observable. Для застосунків, що вже інтенсивно використовують RxJS, селектори класичного NgRx на основі Observable безперешкодно інтегруються з існуючими конвеєрами. ## Стратегія Міграції з Класичного до Signal Store Міграція існуючого застосунку класичного NgRx не вимагає повного переписування. Обидва рішення можуть співіснувати, дозволяючи поступове впровадження. - Почати нові фічі з використанням Signal Store - Мігрувати ізольовані модулі фіч по одному - Залишити складні фічі з великою кількістю ефектів на класичному NgRx, якщо вартість міграції перевищує користь - Використовувати утиліти [NgRx Signals interop](https://ngrx.io/guide/signals) для з'єднання стану на основі Observable та стану на основі сигналів > **Співіснування працює** > > Класичний NgRx Store та Signal Store можуть працювати поруч в одному застосунку. Команда NgRx офіційно підтримує обидва підходи та надає утиліти для взаємодії між ними. ## Коли Обирати Кожен Підхід ### Обирайте Signal Store коли: - Створення нових застосунків Angular 20+ - Управління станом на рівні фіч або компонентів - Пріоритетом є developer experience та зменшення бойлерплейту - Команда має обмежений досвід з NgRx - Важливий розмір бандлу ### Обирайте класичний NgRx коли: - Підтримка існуючих застосунків NgRx - Потрібні суворі логи екшенів та аудиторські сліди - Створення складних робочих процесів з розширеними побічними ефектами - Потрібне повне time-travel налагодження Redux DevTools - Команда має глибоку експертизу NgRx Розуміння основ управління станом в обох підходах готує розробників до роботи з різноманітними кодовими базами Angular. ## Висновок - Signal Store зменшує бойлерплейт управління станом на 60-70% порівняно з класичним NgRx - Класичний NgRx залишається кращим вибором для корпоративних застосунків, що вимагають суворих аудиторських слідів та складних побічних ефектів - Обидва підходи можуть співіснувати в одному застосунку, дозволяючи поступову міграцію - Computed signals Signal Store забезпечують автоматичну мемоїзацію без явних визначень селекторів - Власні фічі store (`signalStoreFeature`) дозволяють повторно використовувати наскрізну функціональність - Для нових проектів Angular 20+ у 2026 році Signal Store має бути вибором за замовчуванням, якщо специфічні вимоги не потребують класичного NgRx --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/angular/ngrx-signal-store-vs-classic-ngrx-comparison