In this lesson, we will see how to fetch the product details using Axios in our SmileCart project.
We can utilize the following API endpoint to fetch the details of a product by its slug.
Throughout this lesson, we will be using infinix-inbook-2 as product_slug.
Import Axios
Let's return to our Product.jsx component and import the Axios library.
Create an async function
We will define an async function named fetchProduct that will be responsible for fetching product details from the API.
axios.get function will send a GET request to the product details endpoint and return a Promise. We use the await keyword to pause the execution until this Promise is resolved or rejected. fetchProduct is declared as an async function to indicate that it involves asynchronous operations.
When you use Axios to make an HTTP request, the response is a JavaScript object having the following attributes:
data: It's an object representing the actual server response data. Axios automatically deserializes the data if it is in JSON format.
status: It's a number representing the status code returned by the server.
statusText: It's a string representing the status message corresponding to the HTTP status code. For example, this would be OK for a status code 200, Continue for 100, Not Found for 404.
headers: It's an object that includes HTTP response header information like content type, caching, cookies, etc.
config: It's an object containing the configuration options that were used to make the request. This includes properties like method, URL, headers, and other options.
request: It's an XMLHttpRequest containing the HTTP request information.
Initialize useEffect for initial data fetch
Now let's create a useEffect hook to trigger the fetchProduct function on the initial render of the product component.
As we have learned already, this useEffect hook will run only once, immediately after the component mounts because its dependency array is empty.
To view the API request getting triggered, open the Network tab of your browser's developer tools.
Press F12 or right-click and select "Inspect" to open the browser's developer tools.
Wrap async code in a try...catch block for error handling
To handle errors gracefully when dealing with asynchronous operations like API requests, we should wrap the code awaiting the promises in a try/catch block.
Here's how you can modify the fetchProduct to include the try/catch block:
To illustrate the importance of the try/catch block, let's consider a situation where an invalid URL is provided.
In this example, error that occurs will be caught by the try/catch block, and we can log the error message to the console. However, if we don't use a try/catch block, and an error occurs during the execution, the error will not be caught and handled. It will lead to an unhandled promise rejection warning. Moreover, on attempting to access response.data.name it will result in a runtime error like "Cannot read properties of undefined (reading 'name')", because there is no valid response object to extract the data.
Remember, when working with asynchronous operations, using try/catch blocks is a best practice to ensure that your code handles errors effectively and maintains the stability of your application.
Initialize state to store API response
To store the product data from the response in a state variable, you can use the useState hook. Let’s create a state variable named product to hold this data.
To update the product state with the response data, you can modify the fetchProduct function as follows:
This is how data in the product state looks like:
Now let's replace all the hardcoded product details present in the Product.jsx component with the data in the product state.
This can be accomplished by destructuring the essential properties from the product state. Furthermore, we will calculate the discount percentage for the product and store it in the variable discountPercentage.
Additionally, we should remove the IMAGE_URLS constant from the src/constants.js file.
toFixed() method rounds to a specified number of decimals.
Next, we will replace the hardcoded product image URLs passed to the Carousel.jsx component with the image URLs from the product APIs. To accomplish this, we need to extract two variables from the product state: image_urls, which contains a list of product image URLs, and image_url, which contains the URL of the default product image. We will then use the append function from ramda.js to append the image_urls and image_url together. Finally, we will pass the combined URLs as a prop to the Carousel.jsx component.
We need to render the Carousel component only if the image_urls is not null. Otherwise, we will show the default product image. We can use the isNotNil function from ramda.js to conditionally render the Carousel component.
In JavaScript, it's a best practice to use camelCase for naming variables to maintain consistency and readability. However, when dealing with API responses where the keys are typically in snake_case, it's often necessary to reassign them to camelCase.
At this point, you may see empty content on the product page as shown below for a brief amount of time:
This happens because the product data is empty when React renders the component for the first time. The component can render correctly only after receiving a proper API response from the server.
To overcome such issues it's a good practice to include a loading indicator while data is being fetched. This prevents the user from interacting with an incompletely rendered UI.
To achieve this, we will create a new state variable named isLoading.
The initial value of isLoading is set to true, indicating that the product component is in the loading state initially. Regardless of whether the API call succeeds or encounters an error, the isLoading state needs to be set to false to indicate the completion of the API request. To ensure this, we have called the setIsLoading function inside the finally block.
Let's implement a loader in the product component to indicate to the user that data is being fetched. We will utilize the isLoading state to dynamically display the loader.
To achieve this, we can use the Spinner component from neetoUI library.
By following these steps, you've successfully updated the product component to show product details through an API. Users will see a loader while the data is being fetched, and once the data is available, the spinner will be replaced with the actual product information.
Here's how the modified product page looks.
Let's commit the new changes:
You can verify the changes here.