DEV Community

Chandelier Axel
Chandelier Axel

Posted on • Edited on

Why you should cancel your API calls in React ?

Explanation

We all know it, every web application make API calls, even the tiniest do so. ( Remember that first Todo app you made with a cat API ?).

You’ll gather some data, render it and do whatever you like on the website.. The real problems arises when either your internet is slow, or the data you need to gather is huge.

Imagine if you need to gather that huge amount of data with barely an Edge connection.. It will take a couple of seconds, at least, to get everything from the API endpoint, right ? What if your user go on another page ?

If you thinked :

« Uh - Nothing, it’s not a big deal anyway. At most the data is uploaded and nothing will happen. »

Well.. You are partially right, NOTHING will happen. Thanks to our job, making mistakes is riskless - most of the time at least, but there’s always place for improvement and it’s our duty to free up as much network space as we can, and make our apps less data consuming.

Without forever teasing, let’s dive straight into it.

Cancelling the calls

First, I created a simple app which render only 2 components :

  • One who will be responsible to make the API call, and to render a button who will handle the redirection.
  • The other component will only render a string.
const App = () => {
    const [apiCallDone, setApiCallDone] = useState(false);

    return (
        <div className="App">
            {apiCallDone
                ? <AnotherComponent />
                : <ApiCallComponent redirectToOtherComponent={setApiCallDone} />
            }
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

As you can see, once the apiCall will be set as true, App.js will re-render and show the other component.

Now let’s take a look at the component that make the actual call.

const ApiCallComponent = ({ redirectToOtherComponent }) => {

    const [result, setResult] = useState([]);

    useEffect(() => {
        fetch('https://pokeapi.co/api/v2/pokemon/12')
            .then(res => res.json())
            .then(data => setResult(data))
    },[]);

    const redirect = () => {
       redirectToOtherComponent(true)
    };

    return (
        <button onClick={redirect} > Let's call the APi </button>
)
};

Enter fullscreen mode Exit fullscreen mode

As you can see I reproduce a really simple component, that will make a call to the Pokémon API as soon as it’ll mount. And the button will trigger the function we passed in the props.

Nothing fancy, right ? We literally made a really minimal representation of all our apps - Gather data, consume it, and possibly show a different view / redirect.

Now let’s add some lags into our call by adding a timeout. Doing so we’ll mimic the slow internet.

  useEffect(() => {
        setTimeout(() => {
            fetch('https://pokeapi.co/api/v2/pokemon/12')
                .then(res => res.json())
                .then(data => setResult(data))
                .catch(err => {
                    // Handle error ..
                })
            }, 3000);
    });
Enter fullscreen mode Exit fullscreen mode

Now let’s try to make our call, and to click on the button within the 3 seconds timer ..

error

Here’s what we were looking for. And I bet you know what this error is. It means that you’re trying to update a component state while the component was unmounted. In our exemple, it’s literally because we didn’t cancel our api call on the unmount.

Fetch cancel

To fix this with the fetch API :

useEffect(() => {
    // First, create a controller, more infos there : https://developer.mozilla.org/en-US/docs/Web/API/AbortController
    const controller = new AbortController();

    setTimeout(() => {
        // Then give it in the fetch options, so the controller is properly linked
        fetch('https://pokeapi.co/api/v2/pokemon/12', {signal: controller.signal})
            .then(res => res.json())
            .then(data => setResult(data))
            .catch(err => {
                // Handle error ..
            })
    }, 3000);

    // Then on the "unmount" of the component, abort the API call ..
    return () => controller.abort();
}, []);
Enter fullscreen mode Exit fullscreen mode

That's all !

Axios

useEffect(() => {
    // More informations about the cancelation for the axios library here : https://github.com/axios/axios#cancellation

    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();

    setTimeout(() => {
        axios('https://pokeapi.co/api/v2/pokemon/12', { cancelToken: source.token })
            .then((res) => setResult(res.data) )
            .catch((err) => {
                // Handle error..
            } )
    }, 3000);

    return () => source.cancel();
}, []);
Enter fullscreen mode Exit fullscreen mode

Congratulation ! You’ve now cleared your console from this filthy errors !

No more excuses when you'll create an API call, you now have all the tools to handle it properly.

You can find the original article on the Othrys website and you can follow my Twitter or tag me here to discuss about this article.

Have a nice day !

Top comments (4)

Collapse
 
soniaortiz profile image
Sonia

Nice Article Axel, I didn't know there was an AbortController option

Collapse
 
julianperezpesce profile image
Julián Pérez Pesce

Thanks, really cool and simple.

Collapse
 
dadtmt profile image
Thomas Culdaut

Using network slow network emulation also do the trick without adding a timeout to your code

Collapse
 
chandelieraxel profile image
Chandelier Axel

Very true, I must use that more often. Thanks for the tip