React

Create a useMousePosition Hook with useEffect and useState in React

Intro

Hooks added a lot of functionality to stateless components. Not only did they add functionality but reusability and sharing of logic across your app. You can combine many of the hooks to leverage each other to build out this reusable logic.

Setup

We'll create a separate file called useMousePosition and import a function that we'll export later. We just expect that it will return a position that is the mouse position. As you can see we have no logic here. We are just requesting a position to render.

The reason we start the function with use is that React provides an eslint plugin that will lint any function that starts with use and make sure you are not violating any of the hook requirements.

import React from "react";
import { useMousePosition } from "./useMousePosition";

const App = () => {
  const position = useMousePosition();

  return <div />;
};

export default App;

Setup our useState

Lets create our function and setup our useState. The useState will take an initial state and we'll just initialize it with {x: 0, y: 0}. This will return an array that we can destructure. It will destructure to the state position, and then a function to update the state.

import { useState, useEffect } from "react";

export const useMousePosition = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  return position;
};

If you're confused about the destructuring of the array here is the equivalent.

const positionState = useState({ x: 0, y: 0 });
const position = positionState[0];
const setPosition = positionState[1];

This is just an ES6 and beyond feature.

Setup our useEffect

Now we setup our useEffect. Within our useEffect we listen on the window for any mouse movements. We need to be sure and clean up any effects that we make and to do that we return a function. This will run when the component is unmounted, or anything in our second argument array changes.

The second argument to useEffect is empty because we only need to attach the window listener once. React will look at the array each render and see if anything has changed. If you didn't pass the array it would always reapply this effect every re-render.

useEffect(() => {
  window.addEventListener("mousemove");

  return () => {
    window.removeEventListener("mousemove");
  };
}, []);

Updating state

You'll notice we didn't apply a function to our addEventListener. When registering an event listener you supply a function. To unregister you need to call removeEventListener with the same function. We no longer have this like we do with classes to store the exact function to unregister.

To do that we can just create a function inside of useEffect. This function will then receive our event and call our setPosition update function with the values.

const setFromEvent = e => setPosition({ x: e.clientX, y: e.clientY });

Putting it all together our hook will look like this.

import { useEffect, useState } from "react";

export const useMousePosition = () => {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const setFromEvent = e => setPosition({ x: e.clientX, y: e.clientY });
    window.addEventListener("mousemove", setFromEvent);

    return () => {
      window.removeEventListener("mousemove", setFromEvent);
    };
  }, []);

  return position;
};

Using the Hook

Now that we're returning our state with an x/y we can render it and move our mouse around and see our div update.

const App = () => {
  const position = useMousePosition();

  return (
    <div>
      {position.x}:{position.y}
    </div>
  );
};

Liked this content?

Get notified more about React!

No Spam! We Promise!

Related Content

Create an Animated 3D Effect Airhorn Button in React Native

In this lesson we'll dive into using some clever offset techniques to make a button that appears to be 3D. We'll add in some animations to control those offsets so that when our button is pressed/released the button animates in and out while playing an airhorn sound.

Create a componentDidMount useEffect hook in React

In this lesson we'll explore how to create useEffect call, and listen to the window for mouse movement. We'll learn about cleaning up our effects, and how to pass an empty array into the second argument of our effect to create a componentDidMount like effect. Finally we'll trigger a mount/unmount to show effects getting cleaned up correctly.

Create a useMousePosition Hook with useEffect and useState in React

In this lesson we'll explore how to combine useEffect with useState to create a reusable hook. It will listen add a window listener for mousemove and update state with each movement.

Create a componentDidMount useEffect hook in React

In this lesson we'll explore how to create useEffect call, and listen to the window for mouse movement. We'll learn about cleaning up our effects, and how to pass an empty array into the second argument of our effect to create a componentDidMount like effect. Finally we'll trigger a mount/unmount to show effects getting cleaned up correctly.