DEV Community

Cover image for 🐶 Truly reactive! Rx+JSX experiment
Kostia Palchyk
Kostia Palchyk

Posted on • Updated 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 ⭐️