Спасибо за эту статью. Это был мой лучший результат в Google по запросу «обработка ошибок ngrx» 😀

Я реализовал это, как вы предложили, но мне не нравилось иметь «глобальное» состояние ошибки. Связь между действием и ошибкой была неясна, что в моем случае привело к побочным эффектам.

Мой подход сейчас следующий:

  1. Иметь абстрактный класс для действий, которые могут иметь ошибку/успех
import { Action } from '@ngrx/store';
type HandlerFunction<T> = T extends void ? () => void : (error: T) => void;
export abstract class ResponsiveAction<SUCCESS_TYPE, ERROR_TYPE> implements Action {
    public abstract type: string;
    private successHandler: HandlerFunction<SUCCESS_TYPE>;
    private errorHandler: HandlerFunction<ERROR_TYPE>;
    public success(handler: HandlerFunction<SUCCESS_TYPE>): void {
        this.successHandler = handler;
    }
    public error(handler: HandlerFunction<ERROR_TYPE>): void {
        this.errorHandler = handler;
    }
    public handleSuccess(result: SUCCESS_TYPE): void {
        if (!this.successHandler) {
            return;
        }
        this.successHandler(result);
    }
    public handleError(error: ERROR_TYPE): void {
        if (!this.errorHandler) {
            return;
        }
        this.errorHandler(error);
    }
}

2. Действие реализует этот класс

export class ResetPassword extends ResponsiveAction<void, string> {
    public readonly type: string = AuthActionTypes.ResetPassword;
    constructor(public payload: { email: string }) {
        super();
    }
}

3. В любом эффекте, который вызывает успех/ошибку для этого действия, вызовите методы дескриптора

@Effect() public resetPassword$: Observable<Action> = this.actions$.pipe(
    ofType<ResetPassword>(AuthActionTypes.ResetPassword),
    switchMap((action: ResetPassword) => {
        return from(myResetPasswordService.sendPasswordMail(action.payload.email))).pipe(
            map(() => {
                // Here
                action.handleSuccess();
                return new ResetPasswordMailSent();
            }),
            catchError(error => {
                // And here
                action.handleError(error.code);
                return of(new Error({ code: error.code }));
            }),
        );
    }),
);

4. Чтобы обработать ошибку/успех для этого конкретного действия, я просто назначаю обработчик там, где это необходимо, и отправляю действие.

// reset-password.page.ts
[...]
    public resetPassword(email: string): void {
        const action = new ResetPassword({ email });
        action.success(() => {
            this.success = true;
        });
        action.error(error => {
            this.error = error;
        });
        this.store.dispatch(action);
    }
[...]

Таким образом, я не «загрязняю» свое состояние ошибками и могу реагировать на ошибку, специфичную для одного явного действия.

Пол Лессинг, мне бы очень хотелось услышать ваше мнение по этому поводу.