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. About Me... • Rainer Hahnekamp ANGULARarchitects.io • Trainings and Consulting

    @RainerHahnekamp Professional NgRx https://www.ng-news.com https://www.youtube.com /c/RainerHahnekamp
  2. NgRx Family Store Effects Entity Data Router Store Store Devtools

    Schematics ESLint Plugin Component Store Component
  3. 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
  4. Use cases • Complicated, local state logic • Multiple instances

    of same components • Different static logic (Decoupling, Shared) • ~Simplified, global state
  5. 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: [] }); } }
  6. 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), })) ); }
  7. Using the ComponentStore @Component({ templateUrl: './holidays.component.html', standalone: true, imports: [...],

    providers: [HolidaysStore], }) export class HolidaysComponent { holidays$ = this.holidaysStore.holidays$; constructor(private holidaysStore: HolidaysStore) { } }
  8. 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), })); } }
  9. 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); } }
  10. 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 }); }
  11. 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); } }
  12. Comparison to store • Same reactive behaviour • No devtools

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

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

    • Non-global state • Early phases of application development
  15. 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