Паттерны управления состоянием

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

Использование BehaviorSubject для состояния

BehaviorSubject из RxJS хранит текущее состояние и позволяет подписчикам получать его обновления.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

interface AppState {
  isLoggedIn: boolean;
  userName: string;
}

@Injectable({ providedIn: 'root' })
export class StateService {
  private stateSubject = new BehaviorSubject<AppState>({ isLoggedIn: false, userName: '' });
  state$ = this.stateSubject.asObservable();

  updateState(partialState: Partial<AppState>) {
    this.stateSubject.next({ ...this.stateSubject.value, ...partialState });
  }
}

Паттерн Facade

Фасад скрывает сложную логику управления состоянием, предоставляя простой API для компонентов.

@Injectable({ providedIn: 'root' })
export class UserFacade {
  constructor(private stateService: StateService) {}

  login(userName: string) {
    this.stateService.updateState({ isLoggedIn: true, userName });
  }

  logout() {
    this.stateService.updateState({ isLoggedIn: false, userName: '' });
  }

  get user$() {
    return this.stateService.state$.pipe(map(state => state.userName));
  }
}

Локальное состояние компонентов

Компоненты могут управлять собственным локальным состоянием с использованием BehaviorSubject или простых переменных.

@Component({
  selector: 'app-toggle',
  template: `
    <button (click)="toggle()">{{ isActive ? 'On' : 'Off' }}</button>
  `
})
export class ToggleComponent {
  private activeSubject = new BehaviorSubject<boolean>(false);
  isActive$ = this.activeSubject.asObservable();

  toggle() {
    this.activeSubject.next(!this.activeSubject.value);
  }

  get isActive() {
    return this.activeSubject.value;
  }
}

Паттерн Redux / NgRx

Для крупных приложений рекомендуется использование глобального хранилища состояния через NgRx. Принципы включают:

  • Store – единственное глобальное состояние.
  • Actions – события, изменяющие состояние.
  • Reducers – функции обработки actions и обновления состояния.
  • Selectors – функции выборки данных из Store.

Пример action и reducer:

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

export const login = createAction('[Auth] Login', (userName: string) => ({ userName }));
export const logout = createAction('[Auth] Logout');

export interface AuthState { userName: string | null; isLoggedIn: boolean; }

export const initialState: AuthState = { userName: null, isLoggedIn: false };

export const authReducer = createReducer(
  initialState,
  on(login, (state, { userName }) => ({ ...state, isLoggedIn: true, userName })),
  on(logout, state => ({ ...state, isLoggedIn: false, userName: null }))
);

Эти подходы позволяют строить предсказуемую архитектуру, где изменения состояния централизованы и легко отслеживаются. Использование реактивных паттернов обеспечивает синхронизацию UI и бизнес-логики, предотвращая несогласованность данных.