Introduction

We all know it, every web application makes 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 arise when either your internet is slow, or the data you need to gather is huge.

Imagine if you need to fetch a 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 goes to another page ?

If you think :

« 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 further teasing, let’s dive straight into it.

Cancelling the calls

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

  • One who will be responsible for making the API call, and 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>
    );
}

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 makes 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>
)
};

As you can see I implemented 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);
    });

Now let’s try to make our call, and 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();
}, []);

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();
}, []);

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

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

Have a nice day !