How to use APIs in Reactjs without dying in the attempt

In our daily work, we constantly need to communicate with some API to obtain data and perform certain actions. In this article, I will explain how to handle APIs in our Reactjs applications in a simple and effective way (as always, using Typescript).

Alright, here we go! Let’s say I have a REST API that returns a list of users. To get this data in my application, I can use the JavaScript fetch method as follows:

interface User { 
  id: number; 
  name: string; 
}
 
const [users, setUsers] = useState<User[]>([]); 

useEffect(() => { 
  fetch('https://nameofapihere/users') 
    .then(res => res.json()) 
    .then((data: User[]) => setUsers(data)); 
}, []); 

return ( 
  <ul> 
    {users.map((user: User) => ( 
      <li key={user.id}>{user.name}</li> 
    ))} 
  </ul> 
)

Here, I call the API inside a useEffect to retrieve the users, and update the local state users with the response. Then, I render a list of users using that state. This way, every time the component is executed, it retrieves the latest data from the API and updates the user interface.

Now, what happens if the API fails or the data takes a while to arrive? In that case, my user interface will show an empty list, which is not the best when it comes to a good user experience. I can improve this by handling the loading state of the API.

interface User { 
  id: number; 
  name: string; 
} 

const [users, setUsers] = useState<User[]>([]); 
const [loading, setLoading] = useState<boolean>(false); 

useEffect(() => { 
  setLoading(true); 
  fetch('https://nameofapi/users') 
    .then(res => res.json()) 
    .then((data: User[]) => { 
      setUsers(data); 
      setLoading(false); 
    }); 
}, []); 

if (loading) return <p>Loading, please wait...</p> 

return ( 
  <ul> 
    {users.map((user: User) => ( 
      <li key={user.id}>{user.name}</li> 
    ))} 
  </ul> 
)

Here, I have an additional loading state that I can use to display a “Loading, please wait…” message while retrieving data from the API. Once the data arrives, it changes loading back to false to hide that message. This small piece of code significantly improves the user experience.

Now, we haven’t thought about how to handle errors from our API, right? Well, we can catch any errors in the catch() promise and display an error message:

interface User { 
  id: number; 
  name: string; 
} 

const [users, setUsers] = useState<User[]>([]); 
const [loading, setLoading] = useState<boolean>(false); 
const [error, setError] = useState<string>(''); 

useEffect(() => { 
  setLoading(true); 
  fetch('https://nameofapi/users') 
    .then(res => res.json()) 
    .then((data: User[]) => { 
      setUsers(data); 
      setLoading(false); 
    }) 
    .catch((err: Error) => { 
      setLoading(false); 
      setError(err.message); 
    }); 
}, []); 

if (loading) return <p>Loading, please wait...</p> 
if (error) return <p>Error: {error}</p>;

This way, if there is an error retrieving the users, I can display an error message instead of the data. Now, by combining the loading state and error handling, I can handle the obtained data in any way I want while providing a friendly user experience.

Finally, I hope this article has expanded your horizon on how to handle an API in a simple but highly effective way in your projects. The key is to properly manage the loading state and any potential errors that may arise, making sure to display appropriate comments to the user.

See you next time with more valuable content!

That your code be as perfect as a summer breeze! Best wishes!

Posted in Blog, DesignTags:
Write a comment