Article thumbnail

🌟 API calls with useFetch hook and AbortController

9m
code improvements
hooks

Intro

The process of creating a useFetch hook to handle asynchronous operations sprinkled with a dose of real-life examples and AbortController.

Prelude

Fetching and processing data from an API is one of the most repetitive activities in web applications.

That's why it's a good idea to make sure that the code you write is readable, consistent and easy to understand for others.

Don't count on developers. Everyone has different habits. It will be much easier to create an abstraction and encapsulate repetitive logic inside.

So let's jump into useFetch hook creation process and see how it behaves in a real example.

1. What do you implement?

It will be a small app for loading users from a free API. You will be able to load users and display their avatars. Also, there will be an option to perform requests for details.

In addition, when the request is still pending you will be able to cancel the operation to save some browser resources after clicking Back button.

Loading

Application demo

2. Looking at spaghetti

It works indeed, but the used approach is dirty like a mug.

Loading

Try to multiply it by the number of requests in the application or add another code - I wish you luck.

Generally, you can hide this logic in something like useUsersFetch - but implementation inside will be the same.

Loading

In addition - it's only users fetch logic. Still, we need to do the same for user details.

Problems with this code:

  • Flags - easy to add a bug
  • A lot to test
  • It's hard to determine what this code does on first look
  • Lack of single responsibility
  • You can access data property when it's not ready to be accessed
  • Repetitive approach and sensitive to a human mistake

Imagine the whole application written like that 😺.

3. Can you do better?

Of course... Just look at the code below and try to understand it. I think it's obvious what is happening immediately.

Loading

Look at if statements. Inside useFetch hook we implement later exhaustiveness checking. It will be discussed soon but in simple words - it prevents us from accessing properties when they aren't available and guide us by hand when writing conditional instructions.

Loading

Typescript power

All benefits:

  • Less code = smaller bundle
  • Better readability
  • Option to develop similar features faster
  • Typesafety
  • Typescript support when writing if statements
  • Consistent code
  • Easy to test and easy to replace

4. Understanding exhaustiveness checking

You must have seen the difference in code and complexity that we discussed earlier. Now you need to understand how to implement this mechanism in Typescript.

It is simple - just create several types that have a common property and make the union of them.

Loading

Right now you are not able to access properties other than type without if statement. This protects you from potential bugs and prevents you from adding code in the "other" style.

5. Abort controller

Before we start implementation there is one more thing to explain - AbortController class.

This class must be created one per request. Created instance exposes signal property and abort function. Signal must be passed to fetch.

When you pass the signal property, then you can cancel the request via abort function. You can do it at any time - depends on your needs.

Loading

In our case, we call abort all time when the useEffect function is executed.

Also it's good to cancel:

  • Duplicated requests
  • When a user leaves the page
  • When components unmount
  • In any other case

When a request is aborted you can see it in the console and network tab.

Loading

In console

Loading

In network tab

All of this will be possible by the usage of your useFetch hook.

6. Adding tests suites

Let's create useFetch.ts file and same file for tests. You will use TDD. You need to cover the following test scenarios with implementation.

Loading

Test implementation is skipped. If you are interested - you can check it in this file or in the example at the end.

7. Implementing useFetch

Firstly, let's start with interfaces.

Loading

Next, we need to create a hook and implement a handler function with a state management transition which includes the option to abort a request.

Loading

Nothing fancy in this code. You just make generic everything that we discussed before.

8. Create renderer

We can also improve rendering. Instead of several if statements - you can create a component for handling that.

Loading

It simplifies rendering. Now it will be the same in the whole application.

9. Implementing our small app

Let's create two hooks that wrap logic for loading users and user details. These hooks will use your newly created useFetch hook.

Loading

Now in UsersService we can use signal in fetch.

Loading

Right now it's time for the final step. Just use created hooks in the component and display dedicated UI.

Loading

Full example

Fork repository to check the full solution or play with it on Codesandbox platform.

Summary

I hope you enjoyed this quick win 🙂.

Together we explored a pretty interesting topic of creating abstractions in your code base and dealing with unneeded requests. Right now you can boost your work a little bit.

Feel free to contact me if you have any questions/proposals. Have a nice day and good health!

I create content regularly!

I hope you found my post interesting. If so, maybe you'll take a peek at my LinkedIn, where I publish posts daily.

Comments

Add your honest opinion about this article and help us improve the content.

created: 10-09-2022
updated: 03-10-2022