Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NgRx Component Store

NgRx Component Store

Slides for my talk on NgRx Component Store

Rainer Hahnekamp

July 20, 2022
Tweet

More Decks by Rainer Hahnekamp

Other Decks in Technology

Transcript

  1. NgRx Component Store NgRx Component Store Rainer Hahnekamp 20.7.2022

  2. About Me... • Rainer Hahnekamp ANGULARarchitects.io • Trainings and Consulting

    @RainerHahnekamp Professional NgRx https://www.ng-news.com https://www.youtube.com /c/RainerHahnekamp
  3. Agenda • NgRx Family • Theory • Selectors • Actions

    • Effects • Miscellaneous
  4. NgRx Family Store Effects Entity Data Router Store Store Devtools

    Schematics ESLint Plugin Component Store Component
  5. Main facts • (Little) sibling to @ngrx/store • Local /

    component state management • State vanishes with component (by default) • Push-based • All logic included in single service
  6. Use cases • Complicated, local state logic • Multiple instances

    of same components • Different static logic (Decoupling, Shared) • ~Simplified, global state
  7. API ComponentStore SideEffects Read Write select(): Observable patchState() setState() updater()

    // like reducer effect()
  8. Defining the state export interface HolidaysState { holidays: Holiday[]; favouriteIds:

    number[]; } @Injectable() export class HolidaysStore extends ComponentStore<HolidaysState> { constructor(private httpClient: HttpClient, private config: Configuration) { super({ holidays: [], favouriteIds: [] }); } }
  9. Selectors export interface HolidaysState { holidays: Holiday[]; favouriteIds: number[]; }

    @Injectable() export class HolidaysStore extends ComponentStore<HolidaysState> { constructor(private httpClient: HttpClient, private config: Configuration) { super({ holidays: [], favouriteIds: [] }); } readonly holidays$ = this.select(({ holidays, favouriteIds }) => holidays.map((holiday) => ({ ...holiday, isFavourite: favouriteIds.includes(holiday.id), })) ); }
  10. Using the ComponentStore @Component({ templateUrl: './holidays.component.html', standalone: true, imports: [...],

    providers: [HolidaysStore], }) export class HolidaysComponent { holidays$ = this.holidaysStore.holidays$; constructor(private holidaysStore: HolidaysStore) { } }
  11. Actions @Injectable() export class HolidaysStore extends ComponentStore<HolidaysState> { // ...

    addFavourite(holidayId: number) { this.patchState((state) => ({ favouriteIds: [...state.favouriteIds, holidayId], })); } removeFavourite(holidayId: number) { this.patchState((state) => ({ favouriteIds: state.favouriteIds.filter((id) => id !== holidayId), })); } }
  12. Using the ComponentStore @Component({ templateUrl: './holidays.component.html', standalone: true, imports: [...],

    providers: [HolidaysStore], }) export class HolidaysComponent { holidays$ = this.holidaysStore.holidays$; constructor(private holidaysStore: HolidaysStore) { } addFavourite(id: number) { this.holidaysStore.addFavourite(id); } removeFavourite(id: number) { this.holidaysStore.removeFavourite(id); } }
  13. Effects @Injectable() export class HolidaysStore extends ComponentStore<HolidaysState> { // ...

    readonly load = this.effect((i$: Observable<void>) => { return i$.pipe( switchMap(() => this.httpClient.get<Holiday[]>(this.#baseUrl).pipe( tapResponse((holidays) => { const finalHolidays = holidays.map((holiday) => ({ ...holiday, imageUrl: `${this.config.baseUrl}${holiday.imageUrl}`, })); this.#setHolidays(finalHolidays); }, console.error) ) ) ); }); #setHolidays = (holidays: Holiday[]) => this.patchState({ holidays }); }
  14. Using the ComponentStore @Component({ templateUrl: './holidays.component.html', standalone: true, imports: [...],

    providers: [HolidaysStore], }) export class HolidaysComponent { holidays$ = this.holidaysStore.holidays$; constructor(private holidaysStore: HolidaysStore) { this.holidaysStore.load(); } addFavourite(id: number) { this.holidaysStore.addFavourite(id); } removeFavourite(id: number) { this.holidaysStore.removeFavourite(id); } }
  15. Comparison to store • Same reactive behaviour • No devtools

    • Scalability issues • Simpler • Nice goodies ◦ patchState ◦ Debounced selectors
  16. Also keep in mind • Disposing of resources is built-in

    • Combination with @ngrx/store possible • Can also manage global state ◦ via {providedIn: 'root'}
  17. When to use? • Instead of services based on BehaviorSubject

    • Non-global state • Early phases of application development
  18. Alternatives Elf @rx-angular/state

  19. Summary • Simple State Management • Reactive Behaviour • Easy

    to upgrade to NgRx Store later
  20. Further Reading/Watching • Original Design Document ◦ https://hackmd.io/zLKrFIadTMS2T6zCYGyHew?view • Official

    Documentation ◦ https://ngrx.io/guide/component-store • Alex Okrushko on Component Store ◦ https://www.youtube.com/watch?v=v5WSUE1_YHM