Refugees Code
React: API Interaction. useEffect
React: API Interaction. useEffect
When we are creating websites, there is a cool term you have come across - 'state'. Think of it like a photo capturing a specific moment of your website. It's a way to keep track of what's happening on your screen at any given time.
For example, imagine you're filling out a form on a website. The words you type into the boxes? That's part of the state. Or maybe there are parts of the website that can be hidden or shown with a click of a button. Whether these parts are visible or not, that's also part of the state.
And it's not just about what you can see. The state also includes the data that the website is handling. Let's say there's a table on the website showing the latest scores for your favorite sports team. The scores in that table are part of the state too.
So, in a nutshell, the state keeps track of what's going on with the user interface and the data it's dealing with. It's a super important concept in web development and makes websites interactive and dynamic. We have already talked about it in the useState class.
For those operations which state depends on external resources, for example, data coming from a ReST API, we will use React's hook useEffect.
Goals
In this class, we will explore the power of the useEffect hook and how it can help you effectively manage side effects in your React applications, with a focus on fetching data from APIs. By the end of this session, you will:
- Understand the concept of side effects in the context of React development and why they are important to manage, particularly when it comes to data fetching.
- Learn how to use the useEffect hook to handle data fetching from APIs, including using libraries like Axios or the built-in fetch API.
- Explore the use of Async/Await in React components to handle asynchronous data fetching.
- Discover how to manage the fetched data in the component's state using the useState hook.
- Learn best practices for cleaning up effects and optimizing performance when working with useEffect.
- Gain the knowledge and confidence to leverage the useEffect hook to build robust, data-driven React components that seamlessly integrate with APIs.
This understanding of the useEffect hook and API integration will be crucial as you progress through the rest of the React course, where you'll learn about more advanced state management techniques and build complex, interactive user interfaces.
useEffect
The useEffect hook is a fundamental part of React's Hooks system, allowing you to perform side effects in functional components. Side effects are any operations that can affect something outside the scope of the current function, such as data fetching, manual DOM manipulations, and subscriptions. Let's start learning how useEffect works by executing this example:
import React, { useState, useEffect } from "react";
function MyComponent() { // Use the useState hook to manage the count state const [count, setCount] = useState(0);
// The first useEffect call runs once, when the component mounts // This is indicated by the empty dependency array [] useEffect(() => { // Log a message to the console when the component mounts console.log("Component mounted!"); }, []);
// The second useEffect call runs whenever the count state changes // This is indicated by the dependency array [count] useEffect(() => { // Log the current value of the count to the console console.log(`The count is now: ${count}`); }, [count]);
return ( <div> <h1>My Component</h1> <p>The count is: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> );}
export default MyComponent;On this component the first useEffect call runs once, when the component is first mounted. This is indicated by the empty dependency array []. Inside this effect, we log a message to the console. The second useEffect call runs whenever the count state changes. This is indicated by the dependency array [count]. Inside this effect, we log the current value of the count to the console.
Fetching data from APIs in React
That's a basic example to understand how useEffect works. Now let put it into practice with a real-world task: fetching data from an API.
import React, { useState, useEffect } from 'react';
function MyComponent() { const [users, setUsers] = useState([]);
// Fetch users data when the component mounts useEffect(() => { const fetchUsers = () => { try { fetch('https://jsonplaceholder.typicode.com/users').then(response => response.json().then(data => setUsers(data) )
} catch (error) { console.error('Error fetching users:', error); } };
fetchUsers(); }, []);
return ( <div> <h1>Users</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> );}
export default MyComponent;We are fetching data from jsonplaceholder.typicode.com/users and then rendering a list of users after when receive the data asynchronously. the fetchUsers function now uses the .then() syntax to handle the asynchronous nature of the fetch call. The first .then() call handles the response from the fetch call and converts it to JSON using response.json(). The second .then() call takes the JSON data and updates the users state using setUsers. The try/catch block remains the same, catching any errors that might occur during the data fetching process. We can also using an external library called axios to fetch the data
import React, { useState, useEffect } from "react";import axios from "axios";
function MyComponent() { const [users, setUsers] = useState([]);
// Fetch users data when the component mounts useEffect(() => { const fetchUsers = async () => { try { const response = await axios.get( "https://jsonplaceholder.typicode.com/users" ); setUsers(response.data); } catch (error) { console.error("Error fetching users:", error); } };
fetchUsers(); }, []);
return ( <div> <h1>Users</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> );}
export default MyComponent;The result is the same. Using an external library to do the same as a native Web API such as fetch is a decision meant for you and your team.
Async/Await in React
Another way to handle asynchronous code is by using async/await. They are syntactic sugar created on top of JavaScript Promises, so instead of using then and catch you can the result of a promise on a variable by invoking it with the word awaitright in front of it.
import React, { useState, useEffect } from "react";
function MyComponent() { const [users, setUsers] = useState([]);
// Fetch users data when the component mounts useEffect(() => { /*typically you would do: fetch("https://jsonplaceholder.typicode.com/users") .then(response => response.json()) .then(data => setUsers(data))*/ const fetchUsers = async () => { const response = await fetch("https://jsonplaceholder.typicode.com/users") const data = await response.json(); setUsers(data); };
fetchUsers(); }, []);
return ( <div> <h1>Users</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> );}
export default MyComponent;The same example but using try/catch to handle errors in API fetching.
import React, { useState, useEffect } from "react";
function MyComponent() { const [users, setUsers] = useState([]);
// Fetch users data when the component mounts useEffect(() => { const fetchUsers = async () => { try { const response = await fetch( "https://jsonplaceholder.typicode.com/users" ); const data = await response.json(); setUsers(data); } catch (error) { console.error("Error fetching users:", error); } };
fetchUsers(); }, []);
return ( <div> <h1>Users</h1> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> );}
export default MyComponent;Cleaning the effect
Everytime we add a new component to the DOM, we say the component is 'mounted'. On the other hand, it is 'unmounted' when we remove it from the DOM. For example, a notification is mounted when it appears on the screen and it is unmounted when it dissapears.
When we add a function to an event linked to a component, that function could generate problems if it's not properly cleaned. useEffect provides a way to clean the function: by clearing the function/removing the event listener on the return statement of the useEffect.
Here's an example of two components, one cleaned and the other uncleaned to see the difference between the two.
import React, { useEffect, useState } from 'react';
const UncleanedComponent = () => { const [count, setCount] = useState(0);
useEffect(() => { const interval = setInterval(() => { console.log('Interval running in uncleaned component'); setCount((count) => count + 1); }, 1000);
// No cleanup function returned }, []);
return ( <div> <h2>Uncleaned Component</h2> <p>Count: {count}</p> </div> );};
const CleanedComponent = () => { const [count, setCount] = useState(0);
useEffect(() => { const interval = setInterval(() => { console.log('Interval running in cleaned component'); setCount((count) => count + 1); }, 1000);
// Clean up the interval when the component is unmounted return () => clearInterval(interval); }, []);
return ( <div> <h2>Cleaned Component</h2> <p>Count: {count}</p> </div> );};
const App = () => { const [showComponents, setShowComponents] = useState(true);
return ( <div> <button onClick={() => setShowComponents(!showComponents)}> {showComponents ? 'Unmount Components' : 'Mount Components'} </button> {showComponents && ( <> <UncleanedComponent /> <CleanedComponent /> </> )} </div> );};
export default App;On this link you can access the example and try it by yourself.
Another example:
import React, { useEffect, useState } from "react";
export default function Resize() { const [windowWidth, setWindowWidth] = useState(window.innerWidth);
useEffect(() => { // Set up the event listener const handleResize = () => { // Handle resize logic setWindowWidth(window.innerWidth); }; window.addEventListener("resize", handleResize);
// Clean up the event listener when the component unmounts return () => { window.removeEventListener("resize", handleResize); }; },[]); // Empty dependency array to run only once
return ( <div> <h1>Window Width: {windowWidth}</h1> </div> );}Exercise
- Based on the React Router practice, implement the productsList fetching data from https://fakestoreapi.com/products
You can use this repo as starting point. Have fun and keep coding!