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

RxJS Recipes

RxJS Recipes

RxJS is one of my personal favorite technologies. But, the problem with RxJS is the very high learning curve. In this talk, we will take a look at some patterns that can be applied to Angular applications and some more advanced recipes that you should be able to use in your own projects!

Kwinten Pisman

December 06, 2019
Tweet

More Decks by Kwinten Pisman

Other Decks in Programming

Transcript

  1. const show$ = this.sourceJokes$.pipe( skip(1), mapTo(true), ); const hide$ =

    this.update$.pipe(mapTo(false)); const showNotificationSettings$ = this.settingsService.settings$.pipe( select(({ showNotifications }) => showNotifications), ); this.showNotification$ = showNotificationSettings$.pipe( switchMap(showNotifications => { if (showNotifications) { return merge(show$, hide$).pipe(startWith(false)); } return of(false); }), );
  2. “In software engineering, a software design pattern is a general,

    reusable solution to a commonly occurring problem within a given context in software design.”
  3. @elmd_ @KwintenP What we need is a list of jokes

    that is cached. This cache should be updated every 10 seconds. If the call fails, we obviously want to retry this call every 5 seconds, but only when we are online. Makes no sense to retry with no internet connection. Now, these jokes should be shown on the screen but only update on demand when the user requests an update… oh and don't forget to notify the user that there is new data available. And obviously everything, the polling interval, the notifications and polling itself must be configurable. In case one of these settings are updated, we must restart everything! Request from your Manager ”
  4. @elmd_ @KwintenP Problem Sub-Problem A Sub-Problem B Solution to Problem

    Solution to Sub-Problem B Solution to Sub-Problem A
  5. Whenever you have a difficult situation, just split it up,

    fix the small parts and reassemble everything back together to get the final solution! SCHRUTE’s TIP #1
  6. What we need is a list of jokes that is

    cached. This cache should be updated every 10 seconds. If the call fails, we obviously want to retry this call every 5 seconds, but only when we are online. Makes no sense to retry with no internet connection. Now, these jokes should be shown on the screen but only update on demand when the user requests an update… oh and don't forget to notify the user that there is new data available. And obviously everything, the polling interval, the notifications and polling itself must be configurable. In case one of these settings are updated, we must restart everything! Request from your Manager @elmd_ @KwintenP ”
  7. Triggers and results should always be streams. If they are

    not streams, create streams yourself. SCHRUTE’s TIP #2
  8. The list of jokes should be updated every 5 seconds.

    WHEN every 5 seconds WHAT cachedJokes$ that updates every 5 seconds ” Requirement #1 HOW when the trigger fires, send HTTP request to fetch the list of jokes @elmd_ @KwintenP
  9. Pattern #1 Work that needs to be executed n times

    (n > 1) const result$ = trigger$.pipe( <flatteningOperator>(_ => work$) ); Repeater Kwinten - Dominic
  10. ” Requirement #2 jokes$ that is updated on demand when

    the trigger fires when the user manually requests the data extracting the most recent version of the data from the cache @elmd_ @KwintenP WHEN HOW WHAT When new data is available, the user has to manually request the latest version of the jokes to be shown on the screen.
  11. Enricher Pattern #2 Lazily enrich a stream with data when

    the trigger fires const result$ = trigger$.pipe( withLatestFrom(enricherSource$), map(([trigger, data]) => <data>), );
  12. showNotifcation$ that hides or shows a notification ” Requirement #3

    when an update is available (or none is available) create two triggers (show and hide) and merge them @elmd_ @KwintenP WHEN HOW WHAT If an update is available, the user should see a notification to update the list of jokes.
  13. Group Aggregator Pattern #3 Whenever the result is an Observable<boolean>

    const FT$ = falseTrigger$.pipe(mapTo(false)); const TT$ = trueTrigger$.pipe(mapTo(true)); const result$ = merge(FT$, TT$);
  14. ” Requirement #4 settings$ that encapsulates state when the user

    changes one of the settings keep and update state when the trigger fires @elmd_ @KwintenP WHEN HOW WHAT The user should be able to manage settings, that can be updated in a dialog.
  15. State Manager Pattern #4 State that needs to be reactively

    managed const result$ = trigger$.pipe( scan((prevState, updates) => { return { …prevState, …updates } }) );
  16. Use the State Manager pattern as a minimalistic alternative to

    state management libraries like redux. SCHRUTE’s TIP #5
  17. It should be possible to toggle the notifications and polling

    ” Requirement #5 jokes$ and showNotification$ conditionally stopping and starting of the notifications and polling @elmd_ @KwintenP WHEN HOW WHAT showNotification or enablePolling changes
  18. Work Decider Pattern #5 Work that needs to be stopped

    and restarted when some trigger fires const result$ = trigger$.pipe( <flatteningOperator>(condition => { if (condition) workA$;
 else workB$; }) );
  19. ”If fetching the list fails we want to retry fetching

    it. If we are offline, we only want to retry if we are back online. Otherwise, retry every 1 seconds with max of 5. Requirement #6 @elmd_ @KwintenP
  20. Whenever you have a difficult situation, just split it up,

    fix the small parts and reassemble it to the solution! SCHRUTE’s TIP #1 REMEMBER
  21. If fetching the list fails we want to retry it.

    ” Requirement #6.1 @elmd_ @KwintenP
  22. If we are offline, we only want to retry if

    we are online.Otherwise, retry every 1 second with a max of 5. ” Requirement #6.2 if we are offline, do A (retry when online), otherwise do B (retry every second, max 5). @elmd_ @KwintenP WHEN HOW WHAT cachedJokes$ when an error occurs (call to the backend fails)
  23. Error Decider Recipe #6 Work that needs to be stopped

    and restarted when some trigger fires const result$ = trigger$.pipe( retryWhen(error$ => { return error$.pipe(switchMap(_ => { if (condition) workA$;
 else workB$; })) )} ); X
  24. SCHRUTE’s TIP #7 When you have a pattern in your

    app that keeps on repeating, transform it into an operator!