DEV Community

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

Posted on • Edited on

8 3

🐶 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

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

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 ⭐️

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay