Handle Media Query in React with hooks
Tags: react, typescript
Published: 2024-04-02 11:34:18

Hi all, I recently had a need to process media query dynamically in a react application, so I wrote a hook to help me with that.

I'll describe a little bit of what it is and a plan of action.

## The Purpose of useMediaQuery
The **useMediaQuery** hook is a custom React hook that allows developers to track the state of a CSS media query. The hook returns a boolean value indicating whether the document currently matches the media query string provided as an argument.

## How It Works
Here’s a step-by-step explanation of the code:

1. **Import React Hooks**: The _useState_ and _useEffect_ hooks are imported from React to manage state and side effects.
2. **Define the Hook**: _useMediaQuery_ is defined as a function that takes a query string as its parameter and returns a boolean.
3. **Initialize State**: A state variable matches is initialized to false, representing whether the media query matches.
4. **Use Effect Hook:** Inside the _useEffect_ hook:

- A _MediaQueryList_ object media is created by calling _window.matchMedia(query)_.

- A conditional check updates the _matches_ state if the current state doesn’t match the _media.matches_ value.

- A _listener_ function is defined to update the _matches_ state whenever the media query status changes.

- The _listener_ is added as an event listener to the _media_ object for the ‘change’ event.

- A cleanup function is returned to remove the event listener when the component unmounts or the _query_ changes.

5. **Return Value:** The hook returns the current value of _matches_.

## The Code

> mq.hook.tsx
```TypeScript
import { useState, useEffect } from "react";

// Define the hook with 'query' parameter typed as a string
const useMediaQuery = (query: string): boolean => {
  const [matches, setMatches] = useState<boolean>(false);

  useEffect(() => {
    const media = window.matchMedia(query);
    if (media.matches !== matches) {
      setMatches(media.matches);
    }

    // Define the listener as a separate function to avoid recreating it on each render
    const listener = () => setMatches(media.matches);

    // Use 'change' instead of 'resize' for better performance
    media.addEventListener("change", listener); 

    // Cleanup function to remove the event listener
    return () => media.removeEventListener("change", listener);

  }, [matches, query]); // Only recreate the listener when 'matches' or 'query' changes

  return matches;
};

export default useMediaQuery;

```

## Usage

> app.component.tsx
``` TypeScript
import { useMediaQuery } from "../hooks/mq.hook";

function App() {
    const isMobile = useMediaQuery("(max-width: 768px)");

    return (
        <div>
            {isMobile ? <h1>"Mobile"</h1> : <h1>"Desktop"</h1>}
        </div>
    )
}

export default App;
```

## Summary
The _useMediaQuery_ hook is a powerful tool for React developers to create dynamic and responsive applications. By leveraging TypeScript, the code ensures type safety, enhancing the development experience and reducing runtime errors. The hook’s design follows best practices, such as defining the listener outside the effect to avoid unnecessary re-creations and using the ‘change’ event for optimal performance.

This hook can be easily integrated into any React project to provide real-time responsiveness based on any media query, making it an essential utility in the responsive design toolkit.