How to easily ignore useEffect HTTP calls with RxJS

Now that React Hooks have been officially released, even more patterns are emerging across the Internet.

useEffect

The useEffect hook’s among the most popular, as it can replace componentDidMountcomponentDidUpdate, and componentWillUnmount.

Most of the initialization, updates, and cleanup logic a component may need can be put inside of useEffect.

An Ugly User Experience

On a recent project, I encountered a scenario where useEffect acted on HTTP requests I was no longer interested in.

Conceptually, the UI was like this:

 
    • On first load, fetch the list of fruits and render a <button> for each one.

 

  • Click a <button> to fetch that fruit’s details.

But watch what happens when I click multiple fruits in a row

 

Way after I stopped clicking, the fruit detail section kept changing!

The Code

Let’s see my custom hook that leverages useEffect.

Here’s the Codesandbox and GitHub links if you wish to follow along. The file is useFruitDetail.js.

import { useEffect, useState } from 'react';
import { getFruit } from './api';
export const useFruitDetail = fruitName => {
  const [fruitDetail, setFruitDetail] = useState(null);
  useEffect(
    () => {
      if (!fruitName) {
        return;
      }
      getFruit(fruitName).then(setFruitDetail);
    },
    [fruitName]
  );
  return fruitDetail;
};

Whenever fruitName changes, we’ll request its details. And we have no way of cancelling a request! So quickly re-running this results in many state changes that we’re no longer interested in.

If you render this to the UI, you get a messy user experience where the detail section keeps flickering until the final request is resolved.

Enter RxJS

Ignoring old requests is trivial with RxJS.

It can do so much more than what I’ll demo here, so I highly recommend you dive into it!

This portion of our code, the effect code, needs to change.

() => {
  if (!fruitName) {
    return;
  }
  getFruit(fruitName).then(setFruitDetail);
}

Instead of a Promise, let’s convert getFruit into an Observable using the RxJS defer function. And instead of .then, we’ll call .subscribe.

import { defer } from 'rxjs';
...
() => {
  if (!fruitName) {
    return;
  }
  defer(() => getFruit(fruitName))
    .subscribe(setFruitDetail);
}

This doesn’t fix the issue yet. We still need to unsubscribe if fruitNamechanges.

According to React’s docs, we can return a function that’ll be executed at the end of our effect. This acts as the cleanup logic.

So something like this:

() => {
  if (!fruitName) {
    return;
  }
  const subscription = defer(() => getFruit(fruitName))
    .subscribe(setFruitDetail);
  return () => {
    subscription.unsubscribe();
  };
}

It Works!

 

This experience is much cleaner!

By clicking another fruit, useEffect sees fruitName change and runs the previous effect’s cleanup logic. As a result, we unsubscribe from the previous fetch call and focus on the current one.

Now our UI patiently waits until the user’s done clicking and the latest fruit’s details return.

Thanks for following this tutorial to the end! I’m on Twitter if you’d like to talk!

Take care,
Yazeed Bzadough
http://yazeedb.com/

Source: https://medium.freecodecamp.org/how-to-easily-cancel-useeffect-http-calls-with-rxjs-d1be418014e8

Written by

Go to the profile of Yazeed Bzadough

Yazeed Bzadough

freeCodeCamp.org

freeCodeCamp.org

Stories worth reading about programming and technology from our open source community.

http://yazeedb.com
Scroll al inicio

Si continuas utilizando este sitio aceptas el uso de cookies. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar