What is @ngrx/effects?
@ngrx/effects is a library for managing side effects in Angular applications using the Redux pattern. It allows you to isolate side effects from your components and services, making your code more predictable and easier to test.
What are @ngrx/effects's main functionalities?
Handling Side Effects
This code demonstrates how to handle side effects using @ngrx/effects. The `loadItems$` effect listens for the `loadItems` action, calls a service to fetch data, and then dispatches either a success or failure action based on the result.
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { MyService } from './my-service';
import * as MyActions from './my-actions';
@Injectable()
export class MyEffects {
loadItems$ = createEffect(() => this.actions$.pipe(
ofType(MyActions.loadItems),
mergeMap(() => this.myService.getAll().pipe(
map(items => MyActions.loadItemsSuccess({ items })),
catchError(() => of(MyActions.loadItemsFailure()))
))
));
constructor(
private actions$: Actions,
private myService: MyService
) {}
}
Combining Multiple Effects
This code demonstrates how to combine multiple effects in a single class. The `loadItems$` effect handles loading items, while the `updateItem$` effect handles updating an item. Both effects listen for their respective actions and call the appropriate service methods.
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { MyService } from './my-service';
import * as MyActions from './my-actions';
@Injectable()
export class MyEffects {
loadItems$ = createEffect(() => this.actions$.pipe(
ofType(MyActions.loadItems),
mergeMap(() => this.myService.getAll().pipe(
map(items => MyActions.loadItemsSuccess({ items })),
catchError(() => of(MyActions.loadItemsFailure()))
))
));
updateItem$ = createEffect(() => this.actions$.pipe(
ofType(MyActions.updateItem),
mergeMap(action => this.myService.update(action.item).pipe(
map(() => MyActions.updateItemSuccess({ item: action.item })),
catchError(() => of(MyActions.updateItemFailure()))
))
));
constructor(
private actions$: Actions,
private myService: MyService
) {}
}
Error Handling
This code demonstrates how to handle errors in effects. The `loadItems$` effect catches any errors that occur during the service call and dispatches a `loadItemsFailure` action with the error information.
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { MyService } from './my-service';
import * as MyActions from './my-actions';
@Injectable()
export class MyEffects {
loadItems$ = createEffect(() => this.actions$.pipe(
ofType(MyActions.loadItems),
mergeMap(() => this.myService.getAll().pipe(
map(items => MyActions.loadItemsSuccess({ items })),
catchError(error => of(MyActions.loadItemsFailure({ error })))
))
));
constructor(
private actions$: Actions,
private myService: MyService
) {}
}
Other packages similar to @ngrx/effects
redux-saga
redux-saga is a library that aims to make side effects (e.g., asynchronous actions like data fetching) easier and more manageable in Redux applications. It uses generator functions to handle side effects, which can make the code more readable and easier to test compared to @ngrx/effects.
redux-thunk
redux-thunk is a middleware that allows you to write action creators that return a function instead of an action. This function can then perform asynchronous dispatches. While simpler than @ngrx/effects, it can become harder to manage as the complexity of side effects grows.
mobx
MobX is a state management library that makes it simple to connect the reactive data of your application with the UI. Unlike @ngrx/effects, MobX uses observables to manage state and side effects, which can be more intuitive for some developers.
17.0.0-beta.0 (2023-10-30)
Bug Fixes
- entity: set correct return type for getSelectors signature with parent selector (#4074) (b3b571e)
- signals: do not create nested signals for STATE_SIGNAL property (#4062) (71a9d7f)
- signals: improve state type and add type tests (#4064) (10c93ed), closes #4065
Features
BREAKING CHANGES
- component: The LetModule is removed in favor of the standalone LetDirective.
BEFORE:
import { LetModule } from '@ngrx/component';
@NgModule({
imports: [
// ... other imports
LetModule,
],
})
export class MyFeatureModule {}
AFTER:
import { LetDirective } from '@ngrx/component';
@NgModule({
imports: [
// ... other imports
LetDirective,
],
})
export class MyFeatureModule {}
- component: The
PushModule
is deprecated in favor of the standalone PushPipe
.
BEFORE:
import { PushModule } from '@ngrx/component';
@NgModule({
imports: [
// ... other imports
PushModule,
],
})
export class MyFeatureModule {}
AFTER:
import { Component } from '@angular/core';
import { PushPipe } from '@ngrx/component';
@Component({
// ... other metadata
standalone: true,
imports: [
// ... other imports
PushPipe,
],
})
export class MyStandaloneComponent {}
- entity: Selectors returned by the
adapter.getSelectors
signature that accepts a parent selector are strongly typed.
BEFORE:
const {
selectIds, // type: (state: object) => string[] | number[]
selectEntities, // type: (state: object) => Dictionary<Book>
selectAll, // type: (state: object) => Book[]
selectTotal, // type: (state: object) => number
} = adapter.getSelectors(selectBooksState);
AFTER:
const {
selectIds, // type: MemoizedSelector<object, string[] | number[]>
selectEntities, // type: MemoizedSelector<object, Dictionary<Book>>
selectAll, // type: MemoizedSelector<object, Book[]>
selectTotal, // type: MemoizedSelector<object, number>
} = adapter.getSelectors(selectBooksState);
- The minimum required version of Angular has been updated
BEFORE:
The minimum required version of Angular is 16.x
AFTER:
The minimum required version of Angular is 17.x
<a name="16.3.0"></a>