Working Demo

The Code

About 4 months ago, I set out to do a little experiment. The plan was to build a sort algorithm visualizer, but the main point wasn’t about algorithms themselves. My goal was to:

See how well RxJS works with React: I was looking into redux-observable as a middleware to handle async and side effects. Before trying out the library, I wanted to see how I would approach using RxJS with React myself. See how “easy-to-reason” RxJS code is: I was under the impression that stream libraries (and functional programming in general) are rather challenging to learn at first, but becomes intuitive once it sinks in.

So the plan was to write a functioning app, then not touch it for an extended period of time, only to return to it later to refactor it. To my delight, using RxJS was painless to work with and was also easy to refactor after 4 months. This post will be a short review of my personal takeaways.

I only experienced about 7 WTFs/minute. Pretty good, pretty good.

Redux is Stuck in My Head

Even after purposefully excluding Redux in my project, I naturally ended up trying to mimic it. I used the CustomEvent web API (aptly name-spaced as “action”) to dispatch events ,

this.goToNextStep = () => {

const action = {

origin: ‘USER’,

request: ‘GO_TO_NEXT_STEP’

}

document.dispatchEvent(new CustomEvent(‘action’, {detail: action}))

}

and create an observable of “action” events, in a React Component <StreamProvider /> , with a subscription that will trigger state updates that gets passed down to the child component <SortVisualizer /> .

this.sortHistory$ = Observable

.fromEvent(document, 'action').map(e => e.detail)

.mergeMap(...) // deal with actions this.sortHistory$

.subscribe(x => {

this.setState({

sortState: x[x.length - 1]

})

})

Streams are Cool

Part of the challenge was to not use any comments. Can my code, relying only on good naming practices and organization, be easy to read?

It’s hard to say anything about the high-level organization of the application code, because even after 4+ months of inactivity, it was easy to recall the whats and whys of my design decisions after a few minutes of digging in.

However, it was really cool to experience how easy it was to follow the parts written in RxJS.

Implementing “Undo” is Easy with RxJS

Creating the “go back one step” button was a breeze using RxJS. First, I tracked the current state of sort progress as a prop in <SortVisualizer /> as the following:

const currentSortState = {

bars: [{bar}, ...] // data that we're sorting

nextStep: {targetIndex: 0, type: 'COMPARE'} // describes what the next sorting step is

}

This prop is coming from the observable in <StreamVisualizer /> . RxJS’s .scan() method made it really simple to achieve this:

this.sortHistory$ = this.actions$

// other stuff

.scan((acc, curr) => {

if (curr.request === 'GO_TO_PREV_STEP') { // if want to go back

return acc.length <= 1 ? acc : acc.slice(0, acc.length -1)

// just remove latest step

} else {

// other stuff

}

})

Handling Async Declaratively with RxJS

Another aspect that I was focusing on was how easy it is to handle async operations with RxJS. Again, I loved it. Streams allowed me to write async code in a more declarative manner.

For example, I wanted to have an “autoplay” feature that will be toggled off whenever a user tries to do something else, such as drag’n’dropping a bar. Instead of having to imperatively say “If event A happens, turn off autoplay. If event B happens, turn off autoplay. If event C…” I was able to easily say “autoplay until any other event happens.”