In this article, we'll explore how to make fetch calls in React using hooks, which gives us greater control over waiting for responses from requests and better error handling.
Before diving into fetch calls, it's important to understand what hooks are in React. A hook is a special function that allows functional components to maintain their own state, enabling them to replicate certain behaviors of class components. The most commonly used hooks in React are useState, which maintains the component's state, useEffect, which allows for actions to be performed when the component mounts, updates, or unmounts, and useCallback, which optimizes performance and avoids expensive calculations.
However, React also allows us to create our own hooks, known as custom hooks, which combine the functionality of existing hooks to suit our specific needs.
In this article, we'll propose a way to handle server requests using a custom hook, where we can control whether the request is pending, whether it has been completed, and, in case of an error, handle it appropriately. This technique provides us with an efficient and flexible way to make network requests in our React applications, improving the user experience and simplifying error handling.
import {useState, useEffect} from 'react'
export default function useFetch(url) {
const [data, setData] = useState('')
const [isLoading, setIsloading] = useState(false)
const [error, setError] = useState(null)
useEffect(()=> {
try{
setIsloading(true)
const getfetch = async ()=> {
const {data: fetchedData} = await fetch(url).then(res=> res.json())
setData(fetchedData)
}
getfetch()
} catch(err){
setError(err)
}
finally {
setIsloading(false)
}
},[])
return {data, error, isLoading}
}
The code above shows the creation of a function called useFetch, which acts as our custom hook. In the initial lines, the three states necessary for the hook's operation are defined. The first is used to store the information obtained in the call, the second acts as a flag to indicate whether the request is ongoing or has completed, and the last state is used to store error information.
Once the states have been defined, the next step is the use of the useEffect hook, which executes a block of code within a try...catch. First, we set the isLoading state to true to indicate that the call is in progress. Then, we create an asynchronous function to perform the fetch request synchronously and assign the value of the response to the data state. If an error occurs, the catch block handles saving the corresponding error state. Finally, regardless of the outcome of the request, the finally block resets the isLoading flag to false, indicating that the request has finished. This code provides us with a robust mechanism for making network requests in our React applications, with the ability to handle loading state and errors efficiently.
After the useEffect, we only need to return the values that we want to expose outside the hook, and the way to do this is by returning an object with values, in this case, data, isLoading, and error.
Here is the implementation of the custom hook we created earlier:
export default function App() {
const {data, isLoading, error} = useFetch('http://fakeApiRest')
if(isLoading) {
return <div>loading...</div>
}
if(error) {
return <ErrorHandler error={error} />
}
return (
<div>{data}</div>
)
}
In this code snippet, we call the hook we created and receive an object that we destructure into three variables: data, isLoading, and error. Then, we have a condition that checks if isLoading is true, in which case a loading message is displayed. There is also a condition to handle errors: if there's an error, the ErrorHandler component is returned. Finally, if the call is successful, the received information from the request is displayed. This implementation allows for effective control of loading state and errors when making network requests in our React application.
This is one way to implement a request, but let me provide some comments on how I would approach or what changes or additions I might make:
More detailed error handling: Instead of simply returning the ErrorHandler component, we could provide more detailed error information, such as a specific error message or suggested actions for the user.
Performance optimization: Depending on the application and the number of requests made, it might be useful to implement some form of data caching or data retrieval strategies to avoid making redundant requests.
Customization of the user interface during loading: In addition to displaying a loading message, we could consider showing a visual loading indicator, such as a spinner or progress bar, to provide a better user experience.
Top comments (0)