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

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() и далее при любом изменении состояния.

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

Комментарии