Assignments Of Day 12
The useEffect Hook
Assignment 1: Fetch Users from an API
Problem:
Create a React app that fetches a list of users from a public API and displays them. Use useEffect to manage the API call.
Solution:
1. Create the React App: First, create a new React app using create-react-app (if you haven't already).
bash
Copy code
npx create-react-app fetch-users
cd fetch-users
npm start
2. Set up the component structure: In the src folder, open App.js and make the necessary modifications.
3. Fetching data using useEffect: We'll use useEffect to fetch the data when the component mounts.
javascript
Copy code
import React, { useState, useEffect } from 'react';
function App() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
// useEffect to fetch data from API
useEffect(() => {
// Make the API call
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(data => {
setUsers(data); // Store the data in the state
setLoading(false); // Update loading state
})
.catch(error => {
console.error('Error fetching data:', error);
setLoading(false); // Stop loading if there was an error
});
}, []); // Empty dependency array means this effect runs once after the initial render
// Render the component
return (
<div>
<h1>Users List</h1>
{loading ? (
<p>Loading...</p>
) : (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
);
}
export default App;
Explanation:
- useState: We use two state variables:
- users to store the fetched users.
- loading to show a loading message while the data is being fetched.
- useEffect: The useEffect hook is used to fetch the data from the API when the component mounts.
- We pass an empty array [] as the second argument to ensure the effect runs only once after the initial render (similar to componentDidMount in class components).
- The fetch function makes an HTTP request to get the list of users from a public API (jsonplaceholder.typicode.com).
- We then update the users state with the fetched data and stop the loading state by setting loading to false.
- Rendering: The list of users is rendered only after the data has been fetched. If the data is still being loaded, a loading message is displayed.
Assignment 2: Change Background Color on Button Click
Problem:
Create a React app where a button changes the background color of the page when clicked. Use useEffect to clean up any side effects.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows.
javascript
Copy code
import React, { useState, useEffect } from 'react';
function App() {
const [color, setColor] = useState('white'); // State to track the background color
// useEffect to change background color when it updates
useEffect(() => {
document.body.style.backgroundColor = color; // Change the background color
// Cleanup function to reset the background color when the component unmounts
return () => {
document.body.style.backgroundColor = 'white';
};
}, [color]); // This effect runs whenever the "color" state changes
// Button to change background color
const handleChangeColor = () => {
const newColor = color === 'white' ? 'lightblue' : 'white'; // Toggle between two colors
setColor(newColor); // Update the color state
};
return (
<div>
<h1>Click the button to change background color</h1>
<button onClick={handleChangeColor}>Change Color</button>
</div>
);
}
export default App;
Explanation:
- useState: We use the color state to keep track of the current background color.
- useEffect: The effect runs whenever the color state changes. Inside the effect:
- The document.body.style.backgroundColor is set to the current color value.
- The cleanup function (return () => {...}) resets the background color to 'white' when the component unmounts or before the effect runs again (if the color state changes).
- Button Click Handler: The handleChangeColor function toggles the color state between 'white' and 'lightblue' when the button is clicked.
Assignment 3: Timer App with Cleanup
Problem:
Create a timer app that counts seconds since the component was mounted. Use useEffect to clean up the timer when the component unmounts.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0); // State to track the time
// useEffect to start the timer when the component mounts
useEffect(() => {
const timerId = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1); // Increment the seconds
}, 1000);
// Cleanup function to stop the timer when the component unmounts
return () => clearInterval(timerId);
}, []); // Empty array ensures the effect runs once, like componentDidMount
return (
<div>
<h1>Timer: {seconds}s</h1>
</div>
);
}
export default Timer;
Explanation:
- useState: The seconds state tracks the number of seconds since the component was mounted.
- useEffect: The effect runs once after the component mounts (because of the empty dependency array []).
- Inside the effect, we set up a setInterval to increment the seconds state every second.
- The cleanup function (return () => clearInterval(timerId)) clears the interval when the component unmounts, preventing a memory leak.
- Timer: The timer starts counting as soon as the component renders, and it keeps incrementing the seconds state every second.
Assignment 4: Conditional Rendering Based on Data Fetch
Problem:
Create a React app that fetches data from an API and conditionally renders content based on whether the data is available or not.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(post => {
setData(post);
setLoading(false);
})
.catch(error => {
console.error('Error fetching data:', error);
setLoading(false);
});
}, []);
if (loading) {
return <p>Loading...</p>;
}
if (!data) {
return <p>No data available</p>;
}
return (
<div>
<h1>{data.title}</h1>
<p>{data.body}</p>
</div>
);
}
export default App;
Explanation:
- useState: We use data to store the fetched post data and loading to track the loading state.
- useEffect: We fetch data from the API when the component mounts. If the data fetch is successful, the data state is updated, and loading is set to false.
- Conditional Rendering:
- If loading is true, we show a loading message.
- If data is null, we show a message indicating that no data is available.
- If the data is available, we display the post's title and body.
These assignments cover different practical uses of the useEffect hook, helping students learn how to handle side effects like data fetching, conditional rendering, and cleanup in React applications.
4o mini
You said:
some more
ChatGPT said:
ChatGPT
Here are some more assignments with detailed step-by-step solutions and explanations on the useEffect hook:
Assignment 5: Component Will Unmount Example (Cleanup)
Problem:
Create a React component that tracks mouse movements and updates the position of the mouse on the screen. Use useEffect to clean up the event listener when the component unmounts.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function MouseTracker() {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
// useEffect to track mouse position
useEffect(() => {
// Function to update mouse position
const handleMouseMove = (event) => {
setMousePosition({ x: event.clientX, y: event.clientY });
};
// Adding the event listener for mouse move
window.addEventListener('mousemove', handleMouseMove);
// Cleanup function to remove the event listener when the component unmounts
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []); // Empty dependency array means the effect runs only once (on mount)
return (
<div>
<h1>Mouse Position</h1>
<p>
X: {mousePosition.x}, Y: {mousePosition.y}
</p>
</div>
);
}
export default MouseTracker;
Explanation:
- useState: The mousePosition state keeps track of the mouse's current position on the screen.
- useEffect:
- Inside useEffect, we add an event listener for mousemove to update the mouse's position whenever it moves.
- The cleanup function (return () => {...}) removes the event listener when the component unmounts to prevent a memory leak.
- Effect Dependencies: Since we pass an empty array [] as the dependency, the effect only runs when the component mounts and cleans up when it unmounts.
Assignment 6: Timer that Stops After 10 Seconds
Problem:
Create a timer that increments every second and stops after 10 seconds. Use useEffect to set up the timer and clean up after the component unmounts.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
if (seconds >= 10) {
return; // Stop the timer once it reaches 10 seconds
}
const timerId = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);
// Cleanup function to stop the timer
return () => clearInterval(timerId);
}, [seconds]); // The effect depends on "seconds"
return (
<div>
<h1>Timer: {seconds}s</h1>
{seconds === 10 && <p>Timer Stopped!</p>}
</div>
);
}
export default Timer;
Explanation:
- useState: We use the seconds state to track how much time has passed.
- useEffect:
- The effect runs whenever the seconds state changes. Inside the effect, we set up an interval that increments the seconds every second.
- When the seconds reach 10, the useEffect return prevents the interval from being set again, thus stopping the timer.
- The cleanup function clears the interval when the component unmounts or before the effect runs again.
- Condition to Stop Timer: The timer will stop updating after 10 seconds, and the message "Timer Stopped!" will be displayed.
Assignment 7: Component Re-fetching Data Based on Dependency Change
Problem:
Create a React app that fetches data from an API when a button is clicked. The data should be re-fetched every time the button is clicked, using useEffect with the button click count as a dependency.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function App() {
const [clicks, setClicks] = useState(0);
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
// useEffect to fetch data when clicks change
useEffect(() => {
if (clicks === 0) return; // Avoid API call on initial load
setLoading(true);
fetch(`https://jsonplaceholder.typicode.com/posts/${clicks}`)
.then(response => response.json())
.then(post => {
setData(post);
setLoading(false);
})
.catch(error => {
console.error('Error fetching data:', error);
setLoading(false);
});
}, [clicks]); // Effect runs when "clicks" state changes
// Handle button click to increment the clicks state
const handleClick = () => {
setClicks(prevClicks => prevClicks + 1);
};
return (
<div>
<h1>Post Data</h1>
<button onClick={handleClick}>Fetch Post #{clicks + 1}</button>
{loading && <p>Loading...</p>}
{data && !loading && (
<div>
<h3>{data.title}</h3>
<p>{data.body}</p>
</div>
)}
</div>
);
}
export default App;
Explanation:
- useState: We have three states:
- clicks to track the number of button clicks.
- data to store the fetched post data.
- loading to show a loading message while fetching data.
- useEffect:
- The effect depends on the clicks state, and it triggers an API call whenever the clicks state changes.
- It fetches data from the API corresponding to the post number based on the current click count.
- When the data is fetched, it updates the data state, and the loading message is hidden.
- Button Click Handler: The handleClick function increments the clicks state when the button is clicked, causing the useEffect to run again and fetch new data.
Assignment 8: Data Fetch with Error Handling
Problem:
Create a React app that fetches data from an API and handles errors using useEffect. Display an error message if the API call fails.
Solution:
1. Set up the component structure: In the src folder, open App.js and modify it as follows:
javascript
Copy code
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(post => {
setData(post);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []); // The effect runs once when the component mounts
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {error}</p>;
}
return (
<div>
<h1>{data.title}</h1>
<p>{data.body}</p>
</div>
);
}
export default App;
Explanation:
- useState: We use three state variables:
- data to store the fetched post data.
- loading to show a loading message while fetching data.
- error to store any error message that occurs during the fetch.
- useEffect:
- The effect runs once when the component mounts (due to the empty dependency array []).
- We fetch data from the API and handle any errors. If the API call fails, the catch block is triggered, setting the error state with the error message.
- If the data is successfully fetched, it updates the data state and sets loading to false.
- Error Handling: If there’s an error, it is displayed in the component. If the data is still loading, a loading message is shown.
