Actions, Reducers и Store

В архитектуре Angular приложения для управления состоянием часто используется паттерн Redux, реализованный через библиотеку @ngrx/store. Основные концепции включают Actions, Reducers и Store.

Actions

Action — это объект, описывающий изменение состояния. Объект содержит тип действия и необязательный payload с данными.

Пример определения action:

import { createAction, props } from '@ngrx/store';

export const addTodo = createAction(
  '[Todo List] Add Todo',
  props<{ title: string }>()
);

export const removeTodo = createAction(
  '[Todo List] Remove Todo',
  props<{ id: number }>()
);
  • Тип действия обычно содержит контекст [Todo List] для удобства отладки.
  • props задаёт структуру данных, передаваемых в action.

Reducers

Reducer — чистая функция, которая принимает текущее состояние и action и возвращает новое состояние.

Пример reducer для списка задач:

import { createReducer, on } from '@ngrx/store';
import { addTodo, removeTodo } from './todo.actions';

export interface TodoState {
  todos: { id: number; title: string }[];
}

export const initialState: TodoState = {
  todos: []
};

export const todoReducer = createReducer(
  initialState,
  on(addTodo, (state, { title }) => ({
    ...state,
    todos: [...state.todos, { id: state.todos.length + 1, title }]
  })),
  on(removeTodo, (state, { id }) => ({
    ...state,
    todos: state.todos.filter(todo => todo.id !== id)
  }))
);

Store

Store — централизованное хранилище состояния приложения. Через него компоненты получают доступ к состоянию и могут отправлять actions для его изменения.

Регистрация store в модуле:

import { StoreModule } from '@ngrx/store';
import { todoReducer } from './todo.reducer';

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

Использование store в компоненте:

import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { addTodo } from './todo.actions';

@Component({
  selector: 'app-todo',
  template: `
    <ul>
      <li *ngFor="let todo of todos$ | async">{{ todo.title }}</li>
    </ul>
    <button (click)="addNewTodo()">Add Todo</button>
  `
})
export class TodoComponent {
  todos$: Observable<{ id: number; title: string }[]>;

  constructor(private store: Store<{ todos: { id: number; title: string }[] }>) {
    this.todos$ = store.select('todos');
  }

  addNewTodo() {
    this.store.dispatch(addTodo({ title: 'New Task' }));
  }
}

Store обеспечивает предсказуемость состояния, упрощает отладку и масштабирование приложения. Actions задают намерения, Reducers обрабатывают эти намерения, а Store хранит актуальное состояние.