How Reactive Programming simplifies complicated Frontend applications

Anna Shavurska, Viseven

How Reactive Programming simplifies complicated Frontend applications

With ♥ by Anna Shavurska, Viseven

Who I am...

Fb: anna.dederkal.1
Twitter: @AnnaShavurska

UI becomes more complicated

A move to Reactiveness

Reactive Code

            let a = 1;
            let b = 2;
            let sum = a + b;
            console.log(sum); // 3
            a = 2;
            console.log(sum); // ? 4          
        

What we're doing

eWizard

What reactive programming is...

Reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change

Wikipedia

Stream - is like an array in time

Stream Example

            button.addEventListener('click', event => {
              console.log(event);
            });
        

Streams:
- variables,
- user inputs,
- properties,
- events, etc.

Propagation of Change

Pull -> Push

Iterator -> Observer

            const hi = 'hi';
            const iterator = hi[Symbol.iterator]();
            iterator.next(); // 'h'
            iterator.next(); // 'i'
            iterator.next(); // undefined
        
            import { from } from 'rxjs';
            const observable = from('hi');
            observable.subscribe((x) => {
              console.log(x);
            });
            // 'h', 'i'
        

Rx - Reactive
eXtensions

An API for asynchronous programming with observable streams

reactivex.io

Simple Example

            button.addEventListener('click', event => {
              console.log(event);
            });
        
            fromEvent(button, 'click')
              .subscribe(event => console.log(event));
        

Pipes

Operators are Pipeable

            
            const example = sourceOne.pipe(
              concat(sourceTwo),
              filter(num => num % 2 === 0)
            );
            example.subscribe(val => console.log(val));
            
        

But WTH I need this Reactive stuff?

Let’s think about writing complex SPA

Managing state stuff is hard

Vuex makes it simple
(not necessarily easy)*

It doesn’t do anything to help you manage async code.

Frontend Development: mostly synchronous or asynchronous?

We tend to think synchronously. We write code in blocks that are read top to bottom, left to right. If this, then this, else this…

But the truth is:

What are we doing with code?

Managing async stuff is harder

RxJS makes it manageable

How Rx helps to manage async stuff?

Drag-n-drop with optimization

Example is here

Steps:

  1. Listen mouse events
  2. Transform mouse events to drag events
  3. Take a draggeable element on dragstart
  4. And create a preview element for it
  5. Move both: preview element and draggeable element
  6. Remove preview element on dragend

Create Streams from Mouse Events

            const mouseDown$ = fromEvent(body, 'mousedown')
             .pipe(map(getCoordsFromEvent));
            const mouseMove$ = fromEvent(body, 'mousemove')
             .pipe(map(getCoordsFromEvent));
            const mouseUp$ = fromEvent(body, 'mouseup')
             .pipe(map(getCoordsFromEvent));
        

And transform them to Drag Events

            const dragStart$ = mouseDown$
              .pipe(flatMap(() => mouseMove$.pipe(takeUntil(mouseup$),take(1))));
            const dragMove$ = mousedown$
              .pipe(flatMap(() => mouseMove$.pipe(takeUntil(mouseup$))));
            const dragEnd$ = dragStart$
              .pipe(flatMap(() => mouseUp$.pipe(take(1))));
        

Then take a dragged element

            const dragElement$ = dragStart$
              .pipe(
                map(({ x, y }) => document.elementFromPoint(x, y)),
                tap(dragEl => dragEl.classList.add('active')),
                share(),
              );
        

And create a preview element

            const previewElement$ = dragStart$
              .pipe(
                withLatestFrom(dragElement$),
                map(([{ x, y }, dragEl]) => createPreviewElement(dragEl, x, y)),
                tap(previewElement => body.appendChild(previewElement)),
                share(),
              );
        

withLatestFrom

Now move a preview element

            const movePreviewElement$ = dragMove$
              .pipe(
                withLatestFrom(previewElement$),
                tap(([{ x, y }, previewEl]) => {
                  movePreviewElement(previewEl, x, y))
                }),
              );
        

And move dragged element itself

            const moveDragElement$ = dragMove$
              .pipe(
                withLatestFrom(dragElement$),
                tap(([{ x, y }, dragEl]) => moveDragElement(dragEl, x, y)),
              );
        

sampleTime

Optimize element movement

            const moveDragElement$ = dragMove$
              .pipe(
                withLatestFrom(dragElement$),
                sampleTime(200),
                tap(([{ x, y }, dragEl]) => moveDragElement(dragEl, x, y)),
              );
        

Then drop the element

            const finishMovement$ = dragEnd$
              .pipe(
                withLatestFrom(previewElement$, dragElement$),
                tap(([_, previewEl, dragEl]) => {
                  previewEl.remove();
                  dragEl.classList.remove('active');
                }),
              );
        

Don't forget to subscibe

            movePreviewElement$.subscribe(() => {});
            moveDragElement$.subscribe(() => {});
            finishMovement$.subscribe(() => {});
        

That's it! So simple

Vue.js + Rx.js

Save Example

vue-rx

RxJS integration for Vue.js.

Handles subscription/unsubscription for you.

Subscriptions

            export default {
              name: 'save-changes',
              subscriptions: {
                  statusMessage: new Observable(...)
              }
            };
        
            //bind to it normally in templates
            <p>{{ msg }}</p>
        

v-stream

            <v-btn v-stream:click="save$">Save</v-btn>
        
            export default {
              domStreams: ['save$'],
              subscriptions () {
                return {
                  statusMessage: this.save$.pipe(map(() => 'Saving'))
                }
              }
            };
        

Vuex Store

            const store = {
              state: { status: Status.Initial }
              mutations: {
                setStatus(state, { status }) { state.status = status; }
              },
              actions: {
                save({ commit }) {
                  commit('setStatus', { status: Status.Saving });
                  saver.save();
                },
                setStatus({ dispatch, commit }, data) {
                  commit('setStatus', data);
                },
              },
            };
        

How to use Vue-rx + Vuex?

Vue-rx - Vuex communication

Dispatch Action

            
              methods: {
                ...mapActions('saveModule', ['save'])
              }
            
        
            export default {
              subscriptions () {
                const status$ = this.$watchAsObservable('status',{immediate:true})
                  .pipe(
                    pluck('newValue'),
                    share()
                  );
                return {
                  statusMessage: status$.pipe(map(this.getStatusMessages)),
                }
              },
              computed: {
                ...mapState('saveModule', ['status']),
              }
            };
        

In what part of
Vue app
Rx may be
used?

Best Practises, Ideas

How to start?

OMG Rx.js is so confusing!

Stop worrying about the operators

Seriously!

Observables are not scarier than promises

            promise.then(resolveFn);
        
            observable.subscribe(nextFn);
        

Then try to use 'map' to chain observables.

What Operator Do I Use???

Remain calm, it's ok

Use operators that you know

Operator Decision Tree

DO NOT Rx All the things

You can build your app as one big observable...
but please don't

Use Rx where it's best suited

Rx - is so much FUN

Thank You

Be Reactive

Fb: anna.dederkal.1
Twitter: @AnnaShavurska