DEV Community

Cover image for ๐Ÿถ Truly reactive! Rx+JSX experiment
Kostia Palchyk
Kostia Palchyk

Posted on • Edited on

๐Ÿถ Truly reactive! Rx+JSX experiment

In the previous post on Recks, we used axios Promises to query github API:

import axios from 'axios';

function App() {
  const url = 'https://api.github.com/repos/axios/axios';

  return <div>
    <h1>Axios</h1>
    <p>{
      axios.get(url).then(response => response.data.description)
    }</p>
  </div>
}
online sandbox

In this post, we'll improve our application UX by:

  • adding a loading indicator
  • displaying an error if such occurs
  • and we'll avoid flickering on fast connections

Preparation

First, let's move from Promise-based axios to Observable-based axios-observable (a wrapper around the former):

import axiosObservable from 'axios-observable';

function App() {
  const url = 'https://api.github.com/repos/ReactiveX/rxjs';

  return <div>
    <h1>RxJS</h1>
    <p>{
      axiosObservable.get(url).pipe(
        map(response => response.data.description)
      )
    }</p>
  </div>
}

The behavior stays the same: what .then did in Promises, now is handled by RxJS map.

With Observables supporting our might, we are good to create Wonders!

N.B.: There's a hidden benefit here! Apart from tons of operators available in RxJS, we also get a request cancelation on component un-mount for free!

Loading indicator

To show a loading indicator before the response is loaded โ€” we simply need to emit it first:

  startWith(<span>Loading...</span>)

startWith operator will emit the given value and after that will proceed with the rest of the events. In our case โ€” we emit a <span/> element. Next value on the stream will substitute it.

Done!

Error handling

Guess what? Rx has an operator for that too!

  catchError(() => of(<i>Error!</i>))

catchError operator will substitute an error event on the stream with another stream. In our case โ€” we'll emit an <i/> if an error is thrown.

Done!

Psst! Hey, want some retry-on-error operators? Or you wanna learn a fancy retry-with-delay technique? Check out my article "Error handling in RxJS" for details!

Flickering

Well, this is a bit harder. We'll need a whole 3 more lines:

zip(
  axiosObservable.request(url),
  timer(500),
  x => x
)

zip will wait for axios.request and timer(500) both to emit a value and then will produce a combined result. x=>x function is needed to ignore value emitted by the timer and take only the axios response.

Elzar from Futurama

BAAM!
Don't you worry, Elzar knows what to do with a spice weasel. It won't be cooked!

All together

Let's recap what we've written:

function App() {
  const url = 'https://api.github.com/repos/ReactiveX/rxjs';

  return <div>
    <h1>RxJS</h1>
    <p>{
      zip(
        axiosObservable.get(url),
        timer(500),
        x => x
      ).pipe(
        map(response => response.data.description),
        startWith(<span>Loading...</span>),
        catchError(() => of(<i>Error!</i>))
      )
    }</p>
  </div>
}
online sandbox

Wasn't that easy?!

Here are the benefits we achieved:

โญ๏ธ loading indication, error handling, anti-flickering in a minute
โญ๏ธ in-place updates with no state
โญ๏ธ automatic request abortion on unmount
โญ๏ธ great extensibility with dozens of RxJS operators ๐Ÿš€

Share your thoughts in the comments!

To try Recks ๐Ÿถ

Clone the template repository:

git clone --depth=1 https://github.com/recksjs/recks-starter-project.git
cd recks-starter-project
npm i
npm start

Or use this online sandbox

The source code is available at github.com/recksjs/recks

The end

header photo by Mitchell Orr on Unsplash

Top comments (2)

Collapse
 
tiagoha profile image
Tiago A

logic and request in the template? ๐Ÿคฎ๐Ÿคข

Collapse
 
kosich profile image
Kostia Palchyk

Hi, Tiago! Thank you for the comment ๐Ÿ™‚

Well, since JSX is a structure of objects, React allows you to do pretty much any wild stuff in their templates too! ๐Ÿคฏ

The given example was rather supposed to show that any child could be a stream, which gives us so many powers! (partially described above)

Would it look cleaner if we moved the request logic to a function? E.g.:

function App() {
  return <div>
    <h1>RxJS</h1>
    <p>{ makeRequest() }</p>
  </div>
}
Enter fullscreen mode Exit fullscreen mode

And the engine is flexible, so we can map the whole request result to a JSX
(this one looks more reactish, imho)

function App() {
  return makeRequest().pipe(
     map(result => 
       <div>
         <h1>RxJS</h1>
         <p>{ result }</p>
       </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Again, with the article examples, I wanted to show the idea of manipulating streams on a level, native to the framework. And I agree that they might not reflect best practices ๐Ÿ™‚

Hope, my clumsy examples won't hide an interesting concept behind them โญ๏ธ