Design Converter
Education
Last updated on Mar 3, 2025
•6 mins read
Last updated on Feb 26, 2025
•6 mins read
Software Development Executive - II
Handling event listeners inside React components efficiently requires a solid understanding of the useEffect hook. Many developers struggle with managing addEventListener properly inside useEffect, often leading to memory leaks, unnecessary re-renders, or event listeners not being removed correctly.
This blog explores the best practices for using useEffect with addEventListener, ensuring efficient event handling and a deeper understanding of React’s rendering behavior.
React’s useEffect hook is crucial for side effects, such as setting up an event listener inside a component. Using addEventListener inside useEffect allows attaching event listeners to DOM elements or the window object, reacting to user interactions dynamically.
Incorrect usage can lead to unintended re-renders, duplicated event listeners, and performance issues. To handle this effectively, understanding dependency arrays, cleanup functions, and how useEffect interacts with renders is necessary.
To visualize how useEffect interacts with addEventListener, consider the following Mermaid flowcharts.
This diagram explains when useEffect runs and how it attaches and cleans up event listeners.
The cleanup function prevents memory leaks by removing the event listener when the component unmounts or before the effect re-runs.
These diagrams illustrate the importance of proper event listener management inside useEffect. Always ensure that the cleanup function removes the event listener to prevent memory leaks and unintended re-executions.
When a React component mounts, it might need to attach an event listener to respond to user actions. Using useEffect ensures that the event listener is added only when needed and cleaned up properly when the component unmounts.
Common scenarios where useEffect and addEventListener are required:
• Handling window resize events
• Listening for keyboard shortcuts
• Capturing mouse movements
• Fetching data on scroll events
A simple use case of useEffect with addEventListener is listening to window resizing.
1import React, { useState, useEffect } from "react"; 2 3const WindowResizeListener = () => { 4 const [width, setWidth] = useState(window.innerWidth); 5 6 useEffect(() => { 7 const handleResize = () => setWidth(window.innerWidth); 8 9 window.addEventListener("resize", handleResize); 10 11 return () => { 12 window.removeEventListener("resize", handleResize); 13 }; 14 }, []); 15 16 return <h1>Window Width: {width}</h1>; 17}; 18 19export default WindowResizeListener;
useEffect runs on the initial render.
addEventListener attaches a resize event.
The cleanup function removes the event listener when the component unmounts.
Without the cleanup function, the event listener would persist even after the component is removed, leading to memory leaks.
The dependency array in useEffect controls when the effect should re-run. It plays a key role in ensuring event listeners are added or removed efficiently.
1import React, { useState, useEffect } from "react"; 2 3const ClickListener = () => { 4 const [count, setCount] = useState(0); 5 6 useEffect(() => { 7 const handleClick = () => setCount((prev) => prev + 1); 8 9 document.addEventListener("click", handleClick); 10 11 return () => { 12 document.removeEventListener("click", handleClick); 13 }; 14 }, []); // Runs only on the first render 15 16 return <h1>Click Count: {count}</h1>; 17}; 18 19export default ClickListener;
• The useEffect hook runs once on the first render due to an empty array as the second argument.
• The event listener is removed when the component unmounts, preventing memory leaks.
• prev in setCount ensures that we get the previous state before updating.
If dependencies are not managed well, every render might trigger the effect unnecessarily.
1useEffect(() => { 2 const handleScroll = () => console.log("Scrolling..."); 3 4 window.addEventListener("scroll", handleScroll); 5 6 return () => window.removeEventListener("scroll", handleScroll); 7});
• Problem: The effect runs on every render, adding multiple event listeners.
• Solution: Use an empty array or a specific dependency.
1useEffect(() => { 2 const handleScroll = () => console.log("Scrolling..."); 3 4 window.addEventListener("scroll", handleScroll); 5 6 return () => window.removeEventListener("scroll", handleScroll); 7}, []); // Runs only on initial render
When using event listeners inside a useEffect hook, forgetting the effect cleanup can cause memory leaks.
1useEffect(() => { 2 window.addEventListener("mousemove", () => console.log("Mouse moved")); 3}, []);
• Problem: The event listener is never removed.
• Solution: Always include a cleanup function.
1useEffect(() => { 2 const handleMouseMove = () => console.log("Mouse moved"); 3 4 window.addEventListener("mousemove", handleMouseMove); 5 6 return () => window.removeEventListener("mousemove", handleMouseMove); 7}, []);
Sometimes, useEffect is used for fetching data when an event occurs.
1import React, { useState, useEffect } from "react"; 2 3const FetchOnClick = () => { 4 const [data, setData] = useState(null); 5 6 useEffect(() => { 7 const fetchData = async () => { 8 const response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); 9 const json = await response.json(); 10 setData(json); 11 }; 12 13 document.addEventListener("click", fetchData); 14 15 return () => document.removeEventListener("click", fetchData); 16 }, []); 17 18 return <pre>{JSON.stringify(data, null, 2)}</pre>; 19}; 20 21export default FetchOnClick;
• Effect runs on the first render, attaching a click event listener.
• fetchData function runs every click, updating the state.
• Cleanup removes the event listener when the component unmounts.
Before hooks, class components used lifecycle methods like componentDidMount and componentWillUnmount.
1class ResizeListener extends React.Component { 2 state = { width: window.innerWidth }; 3 4 handleResize = () => this.setState({ width: window.innerWidth }); 5 6 componentDidMount() { 7 window.addEventListener("resize", this.handleResize); 8 } 9 10 componentWillUnmount() { 11 window.removeEventListener("resize", this.handleResize); 12 } 13 14 render() { 15 return <h1>Width: {this.state.width}</h1>; 16 } 17}
• Class components use lifecycle methods for event listeners.
• componentDidMount and componentWillUnmount mimic useEffect.
Using useEffect with addEventListener efficiently requires understanding the dependency array, cleanup functions, and re-renders. Avoiding unnecessary event listeners, handling memory leaks, and managing data fetching inside useEffect ensures better performance.
When working with event listeners, always:
Use an empty array if the effect should run only once.
Include a cleanup function to prevent memory leaks.
Watch out for unnecessary re-renders by managing dependencies properly.
Mastering these techniques ensures a smooth experience when handling events in React components.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.