Intro to React Hooks- Part 10

In Part 9 of this series, I went over how we can mimic componentDidMount with the useEffect Hook. As a refresher, we do this by simply passing in an empty array as the second parameter to useEffect.

Here in Part 10, we’ll go over how to mimic componentWillUnmount with the useEffect Hook. We’ll continue with the code that we were looking at in Part 9.

Starter Code

To get a better idea of why we would need something like componentWillUnmount, we’ll create a container component for the HookMouse functional component that we went over in the previous post. And in this container, we’ll just add a button that toggles the components’ visibility.

As a refresher, here’s the code for our HookMouse functional component:

// HookMouse.jsimport React, {useState, useEffect} from 'react';function HookMouse() {    const [x, setX] = useState(0)
const [y, setY] = useState(0)
const logMousePosition = e => {
console.log('Mouse event')
setX(e.clientX)
setY(e.clientY)
}
useEffect(() => {
console.log('useEffect called')
window.addEventListener('mousemove', logMousePosition)
}, [])
return (
<div>
Hooks X - {x} Y - {y}
</div>
)
}
export default HookMouse

Example

To get started, we’ll create a new file called MouseContainer.js. See the following code which we’ll review below:

// MouseContainer.jsimport React, {useState} from 'react';
import HookMouse from './HookMouse';
function MouseContainer() { const [display, setDisplay] = useState(true) return (
<div>
<button onClick={() => setDisplay(!display)}>
Toggle display</button>
{display && <HookMouse />}
</div>
)
}
export default MouseContainer

Within this file, we import useState and the HookMouse component, and we create a functional component. Then, we create a display state variable with a setter of setDisplay and the initial value set to true.

Then, in the JSX, we add a button to toggle the display variable between true and false. For the onClick, we call setDisplay passing in the opposite of the current display value. And then for the last line of code in the return statement, if the display variable is set to true then we render the HookMouse component.

Let’s see what we’ve got going on so far. In App.js, only include the MouseContainer component in the return statement and then let’s hop into the browser. You should see the ‘Toggle display’ button and the mouse coordinates, and when the mouse is moved around, the coordinates are updating and our ‘Mouse event’ log statement is printed every time the mouse moves.

Now, if we click on our ‘Toggle display’ button, you’ll see that it effectively un-mounts the component from the DOM. And when we move our mouse around, you’ll see a warning in the console. Below that warning, you’ll see that the ‘Mouse event’ log statement is still being logged. As a reminder, the ‘Mouse event’ log statement is coming from HookMouse.js’s logMousePosition function.

So even though the component has been removed, the event listener which belongs to that component is still listening and executing. And this is what that warning is indicating, basically telling us “You’ve un-mounted the component, but when you move your mouse around, you’re still asking React to update the state variables for the X and Y coordinates. When you un-mount a component, make sure to cancel all subscriptions and listeners- clean up after yourself!”

So, how do we fix this and clean up our code? With Class Components this is pretty straightforward, as you can simply add window.removeEventListener to the componentWillUnmount lifecycle method, like so:

componentWillUnmount() {
window.removeEventListener('mousemove', this.logMousePosition)
}

But how can we mimic the above lifecycle method functionality in useEffect? Well, the function that is passed to useEffect can return a function which will be executed when the component un-mounts. See additions below in bold:

useEffect(() => {
console.log('useEffect called')
window.addEventListener('mousemove', logMousePosition)
return () => {
console.log('Component unmounted')
window.removeEventListener('mousemove', logMousePosition)
}

}, [])

If we now go back to the browser and move our mouse around, you’ll see the X and Y coordinates updating accordingly and the ‘Mouse event’ statements being logged to the console. And if we click our ‘Toggle display’ button, you’ll now see our new statement ‘Component unmounted’ logged to the console. This means that our event listener is now removed, and if you move your mouse around the window, we’re no longer getting that warning or the ‘Mouse event’ log statements from before.

Summary

The takeaway from this example is that when you want to execute some component ‘clean up’ code, you include it in a function and return that function from the function passed to useEffect. That clean up code can be things like cancelling subscriptions or timers, or removing event listeners like we did here in this example.

Thanks as always for reading and see you next time!

Software Engineer/Full Stack Web Developer | Ruby, JavaScript, React, Redux