An Interest In:
Web News this Week
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
- April 18, 2024
Multi-Store DOM Events (Angular)
This series explores how we can keep our code declarative as we adapt our features to progressively higher levels of complexity.
Level 6: Multi-Store DOM Events
Sometimes multiple stores need to react to a single DOM event. Let's say we have a button in our template that sets all colors in all stores to black. If it weren't for Rule 2, we might write a click handler like this:
setAllToBlack() { // `set` is a state change that should come by default with every adapter this.favoriteStore.set(['black', 'black', 'black']); this.dislikedStore.set(['black', 'black', 'black']); this.neutralStore.set(['black', 'black', 'black']);}
But this would log 3 "events" in Devtools back-to-back. Not to mention the total number of imperative statements is 4 now, instead of just the 1 from the template.
To keep our event sources and stores as declarative as possible, we should favor putting logic in state adapters instead of scattered around. It also reduces repetition. So let's add this state change to the adapter:
setAllToBlack: state => ['black', 'black', 'black'],
Our goal with the button is to push the least amount of data possible to a single place in TypeScript. Since 3 stores need the data, we need to create an independent place to push the event to, and have all stores react to that. We also want to annotate that event source. So let's have something like
blackout$ = new Source('[Colors] Blackout');
All the stores can connect this source and state change like this:
setAllToBlack: this.blackout$,
Here's the whole thing with these changes highlighted:
export class ColorsComponent { adapter = createAdapter<string[]>({ // For type inference changeColor: (colors, [newColor, index]: [string, number]) => colors.map((color, i) => i === index ? newColor : color),+ setAllToBlack: state => ['black', 'black', 'black'], selectors: { colors: state => state.map(color => ({ value: color, name: color.charAt(0).toUpperCase() + color.slice(1), })), }, }); initialState = ['loading', 'loading', 'loading'];++ blackout$ = new Source<void>('[Colors] Blackout'); favoriteColors$ = this.colorService.fetch('favorite').pipe( toSource('[Favorite Colors] Received'), ); favoriteStore = createStore( ['colors.favorite', this.initialState, this.adapter], { set: this.favoriteColors$,+ setAllToBlack: this.blackout$, }); dislikedColors$ = this.colorService.fetch('disliked').pipe( toSource('[Disliked Colors] Received'), ); dislikedStore = createStore( ['colors.disliked', this.initialState, this.adapter], { set: this.dislikedColors$,+ setAllToBlack: this.blackout$, }); neutralColors$ = this.colorService.fetch('neutral').pipe( toSource('[Neutral Colors] Received'), ); neutralStore = createStore( ['colors.neutral', this.initialState, this.adapter], { set: this.neutralColors$,+ setAllToBlack: this.blackout$, });}
Here's how it looks:
It is very common for developers to write callback functions so they can dispatch multiple actions for a single event. This is the imperative style and unsurprisingly is often accompanied by forgotten updates and inconsistent state. If you are using NgRx or NGXS, keep your code reactive and dispatch exactly 1 action for each event.
Original Link: https://dev.to/this-is-angular/multi-store-dom-events-angular-a12
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To