When building a UI in React, you'll likely have many events that you want to listen to. That means you'll want to clean them up too. So, what are events, listeners, and why do we want to clean them up?
An event is, generically, something that a user does or the browser does. A user might click a button or resize a window. The browser might receive a message from a server or worker process.
An event listener is set up when we write code that defines the kind of event that might occur and the code that should run when that event is eventually detected. Events are asynchronous, so event listeners are defined as callbacks to those events.
Callbacks are defined to listen to events over time. They're declared and then they stick around, listening. But there usually comes a time when they can stop listening. And if they're not needed anymore, they should be cleaned up. This is because keeping event listeners around isn't free. It takes memory and some processing power from the browser and the host computer.
What makes this more important is that React components will often be re-rendered, and the code to set up event listeners will have the opportunity to run many times. This can create errors by setting up multiple listeners where we're actually expecting a single listener run per event occurrence.
Additionally, React-based UIs are often used in single-page apps that exist within long-lived browser sessions. This creates an environment where memory leaks can become serious more often.
It is possible that we are using event listeners without really thinking them as such. This is because it's such a natural part of how UIs work and what we write to make them function. For instance, we might have a Form
that has a button
that captures a click event:
1import React from 'react'
2
3function Form() {
4 function handleClick(evt) {
5 console.log('do submitting stuff')
6 }
7 return (
8 // form stuff...
9 <button onClick={handleClick}>Submit</button>
10 )
11}
Thankfully, when the Form
component is unmounted and leaves the DOM, the click handler will be automatically removed as well. React takes care of it. It's built in, and you don't have to worry about it. Done.
For anything that's not a built-in React element-based event listener, you'll need to do extra work for the setup and for the cleanup. Let's say that our Form
component now listens for window resize events. It could look like this:
1import React from 'react'
2
3function Form() {
4 React.useEffect(function setupListener() {
5 function handleResize() {
6 console.log('Do resize stuff')
7 }
8 window.addEventListener('resize', handleResize)
9
10 return function cleanupListener() {
11 window.removeEventListener('resize', handleResize)
12 }
13 })
14 return // render...
15}
Note that the cleanupListener
function that the React.useEffect
callback can returns will be called at the time that this React component is unmounted and is the place to do cleanup. This happens via window.removeEventListener
. The removeEventListener
call must reference the exact same function in the removeEventListener
call to remove the listener correctly.
If you're not using React hooks, you can use the class-based React component lifecycle methods instead. Setup is done inside componentDidMount
and cleanup would be done inside componentWillUnmount
.
Be a good citizen. Always cleanup your event listeners. Do this with window.removeEventListener
when your component unmounts. By cleaning up, you'll avoid listening to events multiple times and memory leaks. Save the planet -- clean up your event listeners.