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

What Groups By in Vegas, Stays in Vegas

Sam Julien
September 05, 2019

What Groups By in Vegas, Stays in Vegas

What if I told you that one of the most useful operators in RxJS is also one you probably have never heard of? In this talk, you'll meet the quiet workhorse called "groupBy."

Don't let its understated nature fool you -- groupBy can change your life! The groupBy operator thrives in heavily interactive UIs, like when users rapidly favorite a bunch of items in a list. When combined with some of the other operators like concatMap, groupBy really shines.

In this talk, you'll learn how to harness its power and take your reactive code to the next level. Using a framework-agnostic approach, you'll see how to use groupBy to give users an excellent experience. You'll also learn the *why* behind when to use groupBy and common pitfalls to avoid with it. Of course, you'll also leave with sample code you can take back to work and start building from right away. Viva Las groupBy!

Sam Julien

September 05, 2019
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. @mikeryandev @samjulien SAM JULIEN @samjulien DEVREL AT AUTH0 GDE &

    ANGULAR COLLABORATOR UPGRADINGANGULARJS.COM
  2. @mikeryandev @samjulien HOW DO WE MAP THE BUTTON CLICKS TO

    THE ENDPOINT CALLS? @mikeryandev @samjulien
  3. @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId }) =>

    setButtonEmoji(movieId)), ???(movie => toggleStatus(movie.movieId)) ); @mikeryandev @samjulien
  4. @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId }) =>

    setButtonEmoji(movieId)), ???(movie => toggleStatus(movie.movieId)) ); @mikeryandev @samjulien
  5. @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId }) =>

    setButtonEmoji(movieId)), ???(movie => toggleStatus(movie.movieId)) ); @mikeryandev @samjulien
  6. @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId }) =>

    setButtonEmoji(movieId)), ???(movie => toggleStatus(movie.movieId)) ); @mikeryandev @samjulien
  7. @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId }) =>

    setButtonEmoji(movieId)), mergeMap(movie => toggleStatus(movie.movieId)) ); @mikeryandev @samjulien
  8. @mikeryandev @samjulien @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId

    }) => setButtonEmoji(movieId)), concatMap(movie => toggleStatus(movie.movieId)) );
  9. @mikeryandev @samjulien @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId

    }) => setButtonEmoji(movieId)), switchMap(movie => toggleStatus(movie.movieId)) );
  10. @mikeryandev @samjulien @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId

    }) => setButtonEmoji(movieId)), switchMap(movie => toggleStatus(movie.movieId)) );
  11. @mikeryandev @samjulien @mikeryandev @samjulien const actions$ = dispatcher.asObservable().pipe( tap(({ movieId

    }) => setButtonEmoji(movieId)), switchMap(movie => toggleStatus(movie.movieId)) );
  12. [[, , ], [, , ], [, , ]] array.flat()

    [, , , , , , , , ] @mikeryandev @samjulien
  13. of(of(, , ), of(, , ), of(, , )) observable$.pipe(mergeAll())

    of(, , , , , , , , ) @mikeryandev @samjulien
  14. actions$.pipe( groupBy(action => action.movieId), map(actionsByMovieId$ => { return actionsByMovieId$.pipe( switchMap(action

    => { return toggleStatus(action.movieId); }) ); }), mergeAll() ); @mikeryandev @samjulien
  15. actions$.pipe( groupBy(action => action.movieId), map(actionsByMovieId$ => { return actionsByMovieId$.pipe( switchMap(action

    => { return toggleStatus(action.movieId); }) ); }), mergeAll() ); @mikeryandev @samjulien
  16. actions$.pipe( groupBy(action => action.movieId), map(actionsByMovieId$ => { return actionsByMovieId$.pipe( switchMap(action

    => { return toggleStatus(action.movieId); }) ); }), mergeAll() ); @mikeryandev @samjulien
  17. actions$.pipe( groupBy(action => action.movieId), map(actionsByMovieId$ => { return actionsByMovieId$.pipe( switchMap(action

    => { return toggleStatus(action.movieId); }) ); }), mergeAll() ); @mikeryandev @samjulien
  18. actions$.pipe( groupBy(action => action.movieId), map(actionsByMovieId$ => { return actionsByMovieId$.pipe( switchMap(action

    => { return toggleStatus(action.movieId); }) ); }), mergeAll() ); @mikeryandev @samjulien
  19. groupBy( /* Key Selector */, /* Element Selector */, /*

    Duration Selector */, ) @mikeryandev @samjulien
  20. @mikeryandev @samjulien Returns an observable that will clean up the

    group when it notifies or completes. DURATION SELECTOR @mikeryandev @samjulien
  21. groupBy( /* Key Selector */, /* Element Selector */, /*

    Duration Selector */, ) @mikeryandev @samjulien
  22. groupBy( action => action.movieId, action => action, group$ => /*

    discard group after 15s of silence */, ) @mikeryandev @samjulien
  23. @mikeryandev @samjulien Returns an observable that will clean up the

    group when it notifies or completes. DURATION SELECTOR @mikeryandev @samjulien
  24. @mikeryandev @samjulien Returns an observable that will clean up the

    group when it notifies or completes. DURATION SELECTOR @mikeryandev @samjulien
  25. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ) ); @mikeryandev @samjulien
  26. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ) ); @mikeryandev @samjulien
  27. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ) ); @mikeryandev @samjulien
  28. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ) ); @mikeryandev @samjulien
  29. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ) ); @mikeryandev @samjulien
  30. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() );
  31. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe(
  32. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe(
  33. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() );
  34. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() ); Group By Movie ID
  35. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() ); Save Status
  36. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() ); Flatten Everything
  37. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), map(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ), mergeAll() );
  38. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), mergeMap(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ) );
  39. actions$.pipe( groupBy( action => action.movieId, action => action, actionsByMovieId$ =>

    actionsByMovieId$.pipe( timeoutWith(15000, EMPTY), ignoreElements() ) ), mergeMap(actionsByMovieId$ => actionsByMovieId$.pipe( switchMap(action => toggleStatus(action.movieId)) ) ) );