Exploring the React useEffect hook

Felix TellmannFT

Felix Tellmann / 2020-01-15

3 min read

One key feature of Hooks is allowing functional components to have access to the react lifecycle. In the class-based way of creating stateful components, you have access to the componentDidMount, componentWillMount, componentDidUpdate, and componentWillUnmount events. The events are super useful to add things like eventListeners to the DOM, make API calls, and to fine-tune performance to ensure that only components that need to render, actually render.

If you are new to React Hooks, check out the Official Docs.

With Hooks, we get the useEffect() API which allows you access to the React Lifecycle.

The useEffect function is triggered when the component is first rendered, and after that on every re-render or update to props & state. You can also set (and should set) the useEffect hook to only run conditionally, based on specific changes to improve performance and avoid memory leaks and unnecessary render calls. In terms of the React Lifecycle, the useEffect hook runs after the DOM is loaded, which is great in comparison to accessing the lifecycle in class components, which can block the UI from being rendered.

Additionally, there are also the useMutationEffect and useLayoutEffect hooks which give us even more precise access into the React lifecycle and I will get more into detail in another post.

Let's have a look at the example in plain javascript below

1import { useEffect, useState } from "react";
2
3const MousePosition = () => {
4 const [{x, y}, setCoordinates] = useState({ x: 0, y: 0 });
5
6 useEffect(() => {
7 const onMouseMove = (e) => {
8 setCoordinates({ x: e.clientX, y: e.clientY });
9 };
10
11 if (window) {
12 document.addEventListener("mousemove", onMouseMove);
13 }
14 return () => { document.removeEventListener("mousemove", onMouseMove) }
15 }, []);
16
17 return <>
18 <div>
19 Your cursor coordinates are X: {x}, and Y: {y}
20 </div>
21 </>;
22};

First, we load the useEffect hook from react, and then we initiate it in the component. The hook requires a callback function and an optional array which include the dependencies. The callback function is run after the UI is rendered and the DOM is loaded. Depending on your environment and if you use Server Side Rendering, it is still a good idea to check if the window object has been instantiated to avoid errors during build time.

In the above example we use the useEffect hook to create an eventListener for mousemove to track a global event.

1return () => { document.removeEventListener("mousemove", onMouseMove) }

Lastly, the return part runs on componentWillUnmount and allows you to clean up the eventListener once it is not needed anymore.

You can see that the hook also has an empty array as a second argument. The array contains any conditional dependencies that should trigger the effect to run again. An empty array means the effect only runs once after the first render and never again until dismounted.

Here is the code in action, just move your cursor over the box:

The beauty about using typescript in your dev environment comes once again through type inference and types provided by @types/react which gives you some amazing hints, right as you need them.