Custom Hooks in React: useMediaQuery

Custom Hooks in React: useMediaQuery

Β·

4 min read

The addition of hooks in React 16.8 added massive improvements and made it easier than ever to reuse functionality between components. There are pre-defined hooks like useState and useEffect but, there is also the ability to create custom hooks to suit your needs.

In this tutorial, you'll learn how to create a hook, useMediaQuery, that can help with conditionally rendering JSX based on specified media queries.

What are Custom Hooks?

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

React Docs

Custom hooks are just functions that can be used within other components. Custom hooks are unique because they can handle their own state and side effects. One of the biggest benefits of being able to build your own hooks is that it provides a convenient way to share reusable logic between components that weren't possible before.

The Problem to Solve

Let's say you want to conditionally render a component on a page at a certain media query breakpoint. For example, I encountered this issue at work while implementing a new tablet layout for our application.

Unfortunately, this isn't the most natural thing to do in React since you'd have to do some CSS trickery to hide the element at the right breakpoint. Although this is possible, there is a better way!

Web API: window.matchMedia()

The main utility behind this custom hook is the window.matchMedia() method that returns a MediaQueryList object. The window.matchMedia() method accepts a single parameter, mediaQueryString, that specifies the media query to observe.

The MediaQueryList object contains properties and events that provide the ability to detect media query "targets" and will keep track of changes as the screen resizes. One of those properties, matches, returns a boolean indicating if the document currently matches the specified media query.

Creating the useMediaQuery Hook

To understand the internals of the hook, let's first take a look at the finished code and then break it down. This example uses TypeScript but can easily be applied to a non-TypeScript implementation.

import { useEffect, useState } from "react";

function useMediaQuery(mediaQuery: string): boolean {
    const [isMediaMatch, setIsMediaMatch] = useState(window.matchMedia(mediaQuery).matches);

    useEffect(() => {
        const mediaQueryList = window.matchMedia(mediaQuery);
        const mqlHandler = () => setIsMediaMatch(mediaQueryList.matches);

        mediaQueryList.addEventListener("change", mqlHandler);
        return () => {
            mediaQueryList.removeEventListener("change", mqlHandler);
        };
    }, [mediaQuery]);

    return isMediaMatch;
}

export default useMediaQuery;

Breaking it Down

  1. Define a useMediaQuery function that accepts a single parameter representing the media query string.
  2. Inside the function, define a state variable that will hold the value of the matches property. We also create a useEffect that will handle all the side effects needed to make things work.
  3. Create a mediQueryList variable that will hold the MediaQueryList object.
  4. Register an event listener, mqlHandler, to the mediaQueryList object that will update the state whenever there is a change to the evaluated result of the matches property. In the useEffect cleanup method, we remove the event listener to prevent lingering subscriptions when the component unmounts.
  5. Return the isMediaMatch state which contains the value from the mediaQueryList.matches property.

Using the Hook

Leveraging our custom hook is pretty straightforward. All we have to do is define a variable that holds the boolean returned from the hook and reference that to conditionally render certain JSX based on the size of the page.

import useMediaQuery from "./hooks/useMediaQuery";

export default function App() {
    const showTabletView = useMediaQuery(
        "(min-width: 768px) and (max-width: 1024px)"
    );

    return (
        <div>
            <h1>useMediaQuery</h1>
            {showTabletView ? (<h2>πŸ“±Tablet view</h2>) 
            : (<h2>This is the default view</h2>)}
        </div>
    );
}

Sample component showing how to leverage the useMediaQuery hook

Screen Cast 2021-12-05 at 2.23.45 PM.gif Gif showing the useMediaQuery hook in action based on the code block above

El Fin πŸ‘‹πŸ½

I was pleasantly surprised to see how simple it was to implement a hook like this. I hope this hook proves to be useful for you and, I look forward to creating more articles on other customized hooks I've created for my project needs.

If you enjoy what you read, feel free to like this article or subscribe to my newsletter, where I write about programming and productivity tips.

As always, thank you for reading, and happy coding!

Resources