How to clean up React event listeners

Key takeaways:

  • Event listeners in React enable dynamic responses to user interactions.

  • Improper handling of event listeners can cause memory leaks and performance issues.

  • Use useEffect with cleanup functions to manage event listeners in functional components.

  • Empty dependency arrays in useEffect ensure effects run only once after the initial render.

  • Use useRef for direct DOM manipulation and attaching custom event listeners.

  • Always clean up event listeners during unmounting or re-renders to avoid resource wastage.

  • Proper event listener management ensures responsive, efficient, and bug-free applications.

In React development, managing event listeners effectively is a fundamental practice for building responsive and efficient applications. Event listeners allow your app to respond dynamically to user interactions, such as clicks, keypresses, or window resizing. However, improper handling of these listeners can lead to issues like memory leaks, performance degradation, or unexpected behavior.

This Answer will walk you through best practices for adding and cleaning up event listeners in React, ensuring your components remain optimized and bug-free. Whether you're using functional components with hooks or class components, mastering these techniques will elevate the reliability and maintainability of your React applications.

Using useEffect hook for cleanup

Here is the code for adding and removing event listeners:

 ãF )   95@@ °n‰PNG


IHDR(-SäPLTE""""""""""""""""""2PX=r€)7;*:>H¤-BGEˆš8do5Xb6[eK™®Kš¯1MU9gs3S\I“§:gt'03@{‹V¹ÔT´ÏA}V»Ö@y‰6\fH’¦-CII”¨Eˆ›+;@7_i7_jFŠJ–«K›°H£-BHaÚû,@FCƒ”L³&.0W½ÙN£ºI“¨$)+B‘J•ªR¯È?v†>s>u„S±Ê=qP©ÁP¨ÀP§¿,?D4U^%+-M ¶K˜®%+,2OX+<ALœ²#&&D†˜%,.I•©vôTötRNSIæçJäeÀe¦©IDATxMŽµZEA„ÿÙ³	îî%R¡ïßáTThÇG…»,Á®Å=²Òîmífímnf’A–$â‡>!¦gºôHg½Eߏܵ}	Ý»ý‡º¼kdú§¯Jo—™Î3æL"J¹ ›ÌÕüQ‡$âçļffµ,é€5i9̟¯H¨/mB†‡wÍÜw;D
Ø+&‚W«ª¹¨Dôo@Ê´RI©ÐB¡om.Û³ÀIEND®B`‚‰PNG


IHDRשÍÊePLTE""""""""""""""""""""""""2RZN¢¹J–«3R[J—¬)59YÁÞ0KS4W`Q«ÄLœ²%+-0JR)6::gtC‚“"##?v†U·Ñ?w†<n{&-/YÂß=q:iuBA}A{ŒB‘/IPP§¿=q€K™®_ÔóL³$();lzR¯ÉaÚûI“¨ZÆã3U^1MU3T]ZÅâI“§X¿ÜF‹ž-BGP¨À6[e,@E5ZdO§¿-BHX¿Û+=AW¾Ú,@FW¼ØQªÃ?v…W¼×+<A@yˆ"#$\Ìê4Wa\ÌëS²Ì$(*.EL^ÑñVºÕ6]h#$%GŽ¡#&';jwV¹Ô-CIL›±ZÄá^Ðï>u„S°Ê/HNM ·_Õõ\ËéM ¶8doD…—D†˜>tƒ+=B[Èæ,>C>t‚<o}@y‰0LS.EKT´Î$'(%,.A~ŽW½ÙC’%+,\ÊèC!ätRNS‘í‰œG¾ÖOIDATxl‰ÃB¶Q…Ÿu´ß_ȳ<˦Ýveê²óa6AξŒûv¢{@ÎE'ÞdIÕ!çží ðC—ÔT‹þg1ÂE(ÏñSQsâi
ď…Zÿ·V¹Ð)ég!‰ªhÎùtéº-i}˜µµ<Õ?¶lBZaÄ´4{DÓ⌻_e8¥yǁ­À3ž)Ÿ¥?°f;8.ã¤tÌ=å;	:ã52fKZìlù¨ØšÍ9.ž#ƒÒAÁqÌúÛ®£Vÿ`=$¬Â?_¶¾®ÔqMç.ïJ$
?^q÷ñíۏï.},‚ìsæÝ_TttÔ¾1#‰/(ì—-[è`è`ÌÚïÅðZd5’Ž™›?ÎebZ¿Þˆi.Ûæ™ìq΄+1°}Œ5ùïçd¨G•ÏøIEND®B`‚‰PNG


IHDR  D¤ŠÆAPLTE"""""""""""""""""""""""""""2RZVºÖ_ÔôU·Ñ=r€$()'25]ÎíCƒ•0LS<o}XÀÜX¿Û0JQ=p~D„–<n{VºÕE‡™8do_ÔóEˆšF‹žH‘¥9dp_ÕõH¤I“¨FŒŸ6[e`Ö÷`×øL³/GM_ÓòU¸Ó'02P©Á/IPPªÂX¿Ü&/1;ly3R[`ØøGŽ¡T³Í\ÌêaÚû1OW"##Q«ÄaÙúR®Ç=q€`Öö.EL+=ATµÐ-CIK˜®#&'C‚“^ÐïI”¨&.04U^^Ñð@yˆZÇä$(*[Éç^Ññ,?DR¯É"#$1NV1MTD…—>u„;kxG R¯È/HN&-/@y‰>s>t‚@zŠ]ÍìP¨À$'(D†—]Ïî<n|0JRU·Ò×\¼	tRNS%­ñ'ïó(ò~ÑÝèžIDATx“šC1F_Ý¿MmÛ¶4¶m{ÿ˜¤n†çáÓ	®A$–à$b‘ Heø™TãWÄÂh•šh´:PtZ
Q«0@.`€Þ`4™-V`³Zì&‡A#ÁébkÝÄãñúØ>.''ø`C$FØÏ	‘(±x"6Xác”TÚéL§@Iù;dd-¹|¾P,Ȕ9¡R­ÕÍf3¢¿F½VmMíX§ÚíÍç@Y˜7ÎõºÕN¬=—ŽåÈʪu
}Ö¬«+‘e‡aiq ¤Ö76­íÝ=h
ûZìíîl›ë‡}á¨ʱ¥[F«I9A¹k9¥ÖëäŒ3¢Ã9ΡóžqB~Øáb¸ÃåU_¸^Ü[·ôw†ý{z‡v‡z‡Ù(Š£(Š£(þ›†Šfòq”ÉG–Éïkñ”ÏçŠY¾ÿªfäòÇ~à:*4ÓQ\O>Ÿ‘ ‡¼<דúW£éÍZ|ދÅ7“ñ•ïjTÔäãn”½»¢®`$Hð+ò¿GOñûð*èx‹•ø¥X*|”^ÿdIEND®B`‚‰PNG


IHDR@@·ì:PLTE"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""%+-@y‰W¼×`×ø^ÑñS²ÌC‚“,>C*8<XÁÝaÚûaÙùMŸµ+<AaÙúXÀÜ#%%TµÐLž´=q>u„K›°`ÖöA}L›±8do=r€%+,@yˆ^ÐïS²Ë)59=q€P©ÁU·Ò"#$PªÂ\Êè0JQQªÃ"##U·Ñ#&&_Ôô>t‚>s`Øø_Ôó5Yc1OW5Zd1NV+=B1MU+;@/GM\Ìê*;?3S\)8<2RZ_Õõ+=A]Ïî,@F,@E&-/0KS7alO¦¾9dp8amB~EˆšP¨ÀN¢¹'023T]]Îí?x‡3U^Cƒ•6\gU¸Ó&.0D„–7_iR­ÆH‘¥I”¨M ¶$(*?v…ZÆãX¿Ü-AG#$%[Éç8co[ÈæW½ÙC’'25?v†8bn%*+Lœ²N£º2PX)7;=p~(58^ÒòP§¿4WaQ«ÄT´Ï0JRQ¬ÅT´ÎI“¨6]hR¯ÉT³Í0LSF‹9eqEˆ›E‰œ9gsFŠCƒ”#&'\Ëé`Ö÷&/16\fB‘A{ŒR®Ç]Íì(47%,.*:>*9=9fr:gt7^iU¶Ð?w†ZÇäX¿Û^ÑðQ­ÅH£)6:V¹Ô'034U^E‡™.EL.FMK™®@zŠS×tRNS*Ž×øÖ”ý˜	»½•+üùóԐ,ØúôÀ=VIDATx¤ËµC!Ð‡C|ÿãÚ^yR]ÕMÛáO]ßÔÕÝ0NÈ2ÍËí¿"ªª¢(0Vã(ÀY%PDT-~(m¬ó!âKÞY£~´•üIÒf{³ÛÞáa¼§§ô3—ÕOp&”Ф‰¡xŽ÷#Ÿj­ôÚ¶mméòc)]m¤’‡É)Ƨgfçhk²ñÎÒ ægg¦Ç™šìÐ+X€ÅêuiyVת·k«°²\[ü:,Ø6ØÜjIJ
;»"»;°×XùþÛfÁáÇÍûý“SÎÎÏÏ8=Þo¾;æèÐ(ƒ‹öÓ¥BkÔeûÍ\7p+mîîáþNÚ<ÀQðOÒæùô³´y‚g»ttÐëo•ý½£ŸìVð»Òäsýü¬™ø”&_ðaüïV~à·Ö?­*8àQ ;8¥Á,¸¤‚f¥“1Üx¤†×§ñŸ*œøÑA¯Ôð°a#±³¶¦#ŠnP‘i+¼¶CÈ,ˆÆèäÍ_áNbÑá‚øç•HŽB*ÚÒ¦ L(^<ñÁ‚L6pJ¾P”É¥Ž©¢%"“R,ä9Èe3eRËa1(
¢ßqÇ8َ´ŠmK˱mƶmÛü·yi!èΪYÏuë ÀÏ_Àï?i÷ˆý+òŠÄA|ù{‘˜´?¿_En).JËD¤<€
©¬¢Z\Ts©R*(	¯©JŠ…uX/
4J9š¡5·DEµ4kÇ4‡&i¥V4Ú¡®Ð¯†vsf:àg,¢èBC»î$¶ºÍùá–@ôŠI_?<‘!^ŠÈàÓ½ÇöäõB‘%Làw±FD1ŠÁ¨(F€±øH˜%0Ʊ¿Å؄(¢0ˆÅÄ'Åæ—N.0u„@íY‡PWìIüaNâKš™Ä?ðÓµŒ=Žeœv/c—±ŒÓ0c0÷2Êë:ˆ06R-uÒÄ­\Q̶ää´¼µ6R#
ÆFš³6Òñ·rՁ­ìu˜æmâðž‡Iñi~ –Åü ÃsPþ"±óŸ¼
eiyå£ËPšàãÊò’§¡œÝÒ,S]U¦ºV…ªÖ”©®Z¦êoëë·xzã™âÆSnm¬{ÚºwaلÏ…Å»´Ýõ(mg/®þå½À¿¼ûŒ[§b³µq¶Å&Õ¯¹$ñzȊ‹H>aÌKT1/æø1O‚‰0¾.h͇YþAÓö£
-ê>ۋº«¢XÕ¢î}ߨëÛÑ;ÃöN´ØvÅýÎ¸ÿ1ë×ÄO@&v/Äþ_—ö\ôÇ\í.™½+0”;!fÊ¦´Ó% JY·O”ÂŽ'/Å]_Š;ßÀ'"&Nªn	aQ^”cx¦AáÒIEND®B`‚
Code to cleanup react event listeners

Explanation

Here's an explanation for the code given above:

  • Line 1: We import React, useState, and useEffect hooks from the react package.

  • Line 3: We define a functional component called App.

  • Line 4: We use the useState hook to create a state variable called clickCount initialized to 0. setClickCount is the function to update this state.

  • Lines 7–9: We define a function called handleClick which updates the clickCount state when called.

  • Line 12: We use the useEffect hook to perform side effects in our component.

  • Line 14: Inside the useEffect, we add an event listener for the 'click' event on the document element. When a click occurs, it will call the handleClick function.

  • Lines 17–19: This is the cleanup function that useEffect returns. It removes the 'click' event listener when the component is unmounted, preventing memory leaks.

Note: When you click the button, the handleClick function is invoked, and the component re-renders. During this re-render, the component is considered for unmounting, and the cleanup function specified in the return statement of useEffect is executed. This cleanup function removes the click event listener from the document.

  • Line 20: We provide an empty dependency array ([]) to useEffect, which means it runs once. It ensures that the event listener is set up after the initial render.

  • Lines 22–27: We return JSX elements to render the component's user interface. It displays the current click count and a button that triggers the handleClick function when clicked.

Managing event listeners in class components

In class components, you manage event listeners using React's lifecycle methods. You add listeners in the componentDidMount method and remove them in componentWillUnmount.

import React, { Component } from 'react';
class App extends Component {
handleResize = () => {
console.log('Window resized');
};
componentDidMount() {
// Add event listener
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
// Remove event listener
window.removeEventListener('resize', this.handleResize);
}
render() {
return <div>Resize the window and check the console!</div>;
}
}

Explanation

  • Lines 4–6: Logs "Window resized" to the console whenever the handleResize method is called.

  • Lines 8–11: The listener is tied to the handleResize method, so the method will be called whenever the window is resized.

  • Lines 13–16: This ensures that handleResize is no longer called after the component unmounts, preventing memory leaks or unexpected behavior.

Dynamic event listener

Sometimes, the event listener logic depends on changing variables (e.g., props or state). In functional components, you can handle this by including those variables in the dependency array of useEffect. The cleanup will occur whenever the effect re-runs due to a dependency change.

import React, { useEffect, useState } from 'react';
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const handleClick = () => {
console.log(`Button clicked ${count} times`);
};
// Add event listener
document.addEventListener('click', handleClick);
// Cleanup event listener
return () => {
document.removeEventListener('click', handleClick);
};
}, [count]); // Effect re-runs when `count` changes
return (
<button onClick={() => setCount(count + 1)}>
Click me
</button>
);
}

Explanation

  • Line 4: Tracks how many times the button has been clicked.

  • Lines 6–18: Runs the enclosed function after the component renders or updates. The second argument ([count]) is the dependency array, which ensures the effect runs only when count changes.

    • Lines 7–9: Logs the current count value to the console whenever a click is detected.

    • Line 12: Adds a click event listener to the document and calls handleClick whenever a click event occurs.

  • Lines 15–17: Removes the event listener whenever the component unmounts and the count dependency changes, and the effect is re-run.

Using ref for direct DOM access

For custom or complex event handling, you might need direct DOM access via useRef. This approach is useful for attaching event listeners to elements not managed by React.

import React, { useEffect, useRef } from 'react';
function App() {
const buttonRef = useRef(null);
useEffect(() => {
const handleClick = () => {
console.log('Button clicked!');
};
// Add event listener
buttonRef.current.addEventListener('click', handleClick);
// Cleanup
return () => {
buttonRef.current.removeEventListener('click', handleClick);
};
}, []);
return <button ref={buttonRef}>Click me</button>;
}

Explanation

  • Line 4: The ref will point to the DOM element after it is rendered, enabling direct manipulation or event attachment.

  • Lines 6–9: The useEffect hook executes the effect function after the component renders. The empty dependency array ([]) ensures this effect runs only once (on mount).

  • Line 12: Attaches a click event listener to the button element referenced by buttonRef.

  • Lines 15–17: Removes the click event listener during cleanup. This happens when the component unmounts or before the effect re-runs (if it had dependencies).

Conclusion

Proper management of event listeners in React is essential for optimal application performance and avoiding memory leaks. Using hooks like useEffect to add and clean up event listeners ensures that side effects are handled safely and efficiently. By incorporating these best practices, developers can maintain the smooth operation of their applications while minimizing resource wastage.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


Why should we remove event listeners?

We remove event listeners to prevent memory leaks, redundant executions, and performance degradation.


How do I remove old event listeners?

Remove old event listeners by calling removeEventListener with the same event type, handler, and target used during addition.


Should you use event listeners in React?

Yes, use event listeners in React to handle interactions, but manage them carefully to prevent memory leaks and ensure efficiency.


Free Resources

HowDev By Educative. Copyright ©2025 Educative, Inc. All rights reserved