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:
- Import React Hooks: The useState and useEffect hooks are imported from React to manage state and side effects.
- Define the Hook: useMediaQuery is defined as a function that takes a query string as its parameter and returns a boolean.
- Initialize State: A state variable matches is initialized to false, representing whether the media query matches.
- 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.
- Return Value: The hook returns the current value of matches.
The Code
mq.hook.tsx
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
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.
Top comments (1)
Since React 18, we can also use
useSyncExternalStore
hook provided by React!Here is an example bellow.