useEffect is one of React's built-in hooks that allows you to perform side effects in function components. Think of side effects as operations that affect something outside the scope of the current function - like data fetching, subscriptions, or manually changing the DOM.
The cleanup function prevents memory leaks by cleaning up resources before the component unmounts or before the effect runs again:
useEffect(() => {// Set up subscriptionconstsubscription=ExternalAPI.subscribe();// Clean up subscriptionreturn () => {subscription.unsubscribe(); };}, []);
// WRONG: userId should be in dependency arrayuseEffect(() => {console.log(`User ${userId} profile viewed`);}, []); // Missing userId dependency// RIGHTuseEffect(() => {console.log(`User ${userId} profile viewed`);}, [userId]);
Unnecessary Dependencies
// WRONG: Including function that doesn't changefunctionComponent() {constformatData= (data) =>`Formatted: ${data}`;useEffect(() => {console.log(formatData("test")); }, [formatData]); // formatData is recreated each render}// RIGHT: Move function inside effect or use useCallbackfunctionComponent() {useEffect(() => {constformatData= (data) =>`Formatted: ${data}`;console.log(formatData("test")); }, []);}
Ignoring Cleanup
// WRONG: No cleanup for timeruseEffect(() => {consttimerId=setInterval(() => {console.log("This runs every second"); },1000);}, []);// RIGHT: Clean up timeruseEffect(() => {consttimerId=setInterval(() => {console.log("This runs every second"); },1000);return () =>clearInterval(timerId);}, []);
Running Effects Unnecessarily
// WRONG: Effect runs on every renderuseEffect(() => {document.title =`Hello, ${name}`;}); // No dependency array// RIGHT: Effect runs only when name changesuseEffect(() => {document.title =`Hello, ${name}`;}, [name]);
// First fetch the userconst { data: user } =useSWR(`/api/users/${userId}`, fetcher);// Then fetch the user's posts using the user's dataconst { data: posts } =useSWR(user ?`/api/posts?userId=${user.id}`:null, fetcher);
Data fetching (use SWR, TanStack Query, or React Server Components)
State updates that could be derived during render
User events (use event handlers instead)
Performance optimizations
Setup that doesn't need cleanup or synchronization
I apologize for missing the exercises section. You're right that I should have included them as in the useState example. Here are three exercises for useEffect with their solutions:
Objective: Create a component that updates the document title when a user types into an input field.
Detailed Instructions:
Create a new file called title-updater.tsx in your components folder.
Import React, useState, and useEffect.
Create and export a functional component named TitleUpdater.
Inside your component:
Create a state variable called title with an initial value of "React App".
Use an input field that updates the title state when the user types.
Use useEffect to update the document title whenever the title state changes.
Make sure to reset the document title when the component unmounts.
Test that the page title in the browser tab changes as you type.
Starter Code:
import { useState, useEffect } from"react";constTitleUpdater= () => {// Add your state declaration here// Add your useEffect herereturn <div>{/* Add your input field here */}</div>;};exportdefault TitleUpdater;
Exercise 2: Window Size Tracker
Objective: Create a component that displays the current window width and height, updating when the window is resized.
Detailed Instructions:
Create a new file called window-size-tracker.tsx in your components folder.
Import React and useEffect.
Create and export a functional component named WindowSizeTracker.
Inside your component:
Create state variables to store the window's width and height.
Use useEffect to:
Set the initial width and height when the component mounts.
Add an event listener for the window's resize event.
Update the state when the window is resized.
Clean up the event listener when the component unmounts.
Display the current window size in your component.
Starter Code:
import { useState, useEffect } from"react";constWindowSizeTracker= () => {// Add your state declarations here// Add your useEffect herereturn <div>{/* Display the window size here */}</div>;};exportdefault WindowSizeTracker;
Exercise 3: Timer Component
Objective: Create a simple timer component that counts up every second and can be started, paused, and reset.
Detailed Instructions:
Create a new file called timer.tsx in your components folder.
Import React, useState, and useEffect.
Create and export a functional component named Timer.
Inside your component:
Create a state variable for the elapsed time (in seconds), starting at 0.
Create a state variable for the timer status (running or paused).
Use useEffect to:
Set up an interval that increments the elapsed time every second when the timer is running.
Clear the interval when the timer is paused or when the component unmounts.
Add buttons to start, pause, and reset the timer.
Display the elapsed time in a readable format (e.g., MM:SS).
Starter Code:
import { useState, useEffect } from"react";constTimer= () => {// Add your state declarations here// Add your useEffect here// Add helper functions for the buttons// Add a function to format the time displayreturn <div>{/* Add the timer display and buttons here */}</div>;};exportdefault Timer;