Перейти к содержанию

Store

NgRx Store (или просто хранилище) хранит в себе глобальное состояние Angular приложения в виде одного большого объекта.

В приложении может быть только одно хранилище.

Хранилище в NgRx представлено сервисом Store и выполняет следующие функции:

  • хранение глобального состояния приложения;
  • обновляет состояние в ответ на действие, принимаемое через метод dispatch();
  • предоставление доступа к состоянию.

Формирование глобального состояния в NgRx Store происходит путем объединения более мелких состояний, которые возвращают зарегистрированные в приложении редюсеры. Делается это с использованием ActionReducerMap<State>.

users.reducer.ts

export interface State{...}
export function usersReducer(state: State = initialState, action: UsersUnion){...}

articles.reducer.ts

export interface State{...}
export function articlesReducer(state: State = initialState, action: ArticlesUnion){...}

index.ts

import * as Users from './reducers/users.reducer'
import * as Articles from './reducers/articles.reducer'

export interface State {
  users: Users.State
  articles: Articles.State
}

export const reducers: ActionReducerMap<State> = {
  users: Users.usersReducer,
  articles: Articles.articlesReducer
}

app.module.ts

import { reducers } from './store/reducers/index'

@NgModule({
  imports: [StoreModule.forRoot(reducers)]
})
export class AppModule {}

Ключи верхнего уровня иерархии глобального объекта состояния задаются разработчиком самостоятельно.

В последнем примере состояние определяется в корневом модуле. Но также NgRx Store может формироваться из состояний, определенных для второстепенных модулей.

users.module.ts

import { usersReducer } from './reducers/users.reducer'

@NgModule({
  imports: [StoreModule.forFeature('users', usersReducer), UsersModule]
})
export class UsersModule {}

app.module.ts

@NgModule({
    imports: [
    StoreModule.forRoot({}),
    UsersModule
    ],
    ...
})
export class AppModule{}

Для регистрации редюсеров на уровне второстепенных модулей используется метод forFeature() модуля StoreModule.forFeature(). При этом корневой модуль может вообще не иметь собственных редюсеров.

В случае если второстепенный модуль загружается асинхронно, то определенное для него состояние динамически добавится в глобальный объект после его полной загрузки. Если модуль не будет загружен вообще, то и в глобальном хранилище ничего связанного с ним тоже не будет.

Доступ к глобальному состоянию осуществляется через экземпляр сервиса Store, прямое обращение к которому возвращает объект Observable.

articles.actions.ts

import { Action } from '@ngrx/store'

export enum ArticlesActions {
  LoadArticle = '[Articles Page] LoadArticle',
  PublishArticle = '[Articles Page] PublishArticle'
}

export interface Article {
  id: number
  title: string
  published: boolean
}

export class LoadArticle implements Action {
  readonly type = ArticlesActions.LoadArticle

  constructor(public payload: { article: Article }) {}
}

export class PublishArticle implements Action {
  readonly type = ArticlesActions.PublishArticle

  constructor(public payload: { id: number }) {}
}

export type ArticlesUnion = LoadArticle | PublishArticle

articles.reducer.ts

export interface State{
    articles: {[id: number]: Article},
    count: number;
}

const initialState: State = {
    articles: {},
    count: 0
};

export function articlesReducer(state: State = initialState, action: ArticlesUnion){
    switch(action.type){
    case ArticlesActions.LoadArticle:
        return {
        ...state,
        articles: {...state.articles, [action.payload.article.id]: action.payload.article}
        };
    case ArticlesActions.PublishArticle:
        return {
        ...state,
        articles: {...{published: true, ...state.article[action.payload.id]}, ...state.articles
        };
    default:
        return state;
    }
}

app.component.ts

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  constructor(private store: Store) {
    this.store.subscribe(state => console.log(state))

    this.store.dispatch(new LoadArticle({ article: { id: 1, title: 'Learn NgRx', publish: false } }))

    this.store.dispatch(new PublishArticle({ id: 1 }))
  }
}

Значение NgRx Store передается обработчику непосредственно в момент вызова метода subscribe() и далее при любом изменении состояния.

Для доступа к определенным частям состояния или вычисления новых данных на основе уже имеющихся в хранилище, используйте селекторы.