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.
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";23const MousePosition = () => {4 const [{x, y}, setCoordinates] = useState({ x: 0, y: 0 });56 useEffect(() => {7 const onMouseMove = (e) => {8 setCoordinates({ x: e.clientX, y: e.clientY });9 };1011 if (window) {12 document.addEventListener("mousemove", onMouseMove);13 }14 return () => { document.removeEventListener("mousemove", onMouseMove) }15 }, []);1617 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.