React basics: creating a custom media query hook

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
Search for a command to run...

I'm a full-stack developer from South Africa 🇿🇦. I love writing about JavaScript, HTML and CSS.
No comments yet. Be the first to comment.
Most of you know me for my consistency, a golden arrow in my blog series. I've written 1000 articles in 1008 days! Almost an article a day, and my honeymoon was the only holiday I ever took. I'm super proud of this achievement; it has been a fantasti...

It's not the first time I'll be talking about community. I think it's an essential aspect of any successful tool. This shows in my previous explorations of Astro, Medusa, and now Vendure as well. All these products thrive in a super open, welcoming, ...

The cool part about Vendure is how easy it is to set up and how abstract each layer is. Basically, we get the following elements: External database Server Worker Admin UI Frontend While this is amazing, it also brings a bit of complexity when it co...

The previous article looked at customizing Vendure on a data and process level. In this article, we'll look at customizing emails, as they are often a big part of a webshop system. We'll be looking at two different layers of customization for customi...

Even though Vendure is a pretty significant project out of the box, in some cases, we might want to go in and modify some elements to work to our specific use case. In this article, I'll take a high-level look at some elements we can customize within...

So far, we have checked out some pre-defined basic hooks in React.
But there is another way you can go about this: creating your own custom hooks.
Let's put it to the test and see why we even want to extract something to be its own hook?
I've created a sample GitHub repo that you can use to play along.
Open up the App.js file, and let's say we want to show a conditional text to desktop users and small screen users.
import { useEffect, useState } from 'react';
function App() {
const [isDesktop, setIsDesktop] = useState(false);
useEffect(() => {
const media = window.matchMedia('(min-width: 960px)');
const listener = () => setIsDesktop(media.matches);
listener();
window.addEventListener('resize', listener);
return () => window.removeEventListener('resize', listener);
}, [isDesktop]);
return (
<div className='App'>
{isDesktop ? <h1>Desktop</h1> : <h1>Small screen</h1>}
</div>
);
}
export default App;
What we do here is create a simple boolean state called isDesktop.
Then we leverage the useEffect to listen to our window and look for a media query that matches (min-width: 960px).
We then set the desktop variable to true or false, based on what the media query evaluates.
Lastly, we add a listener to the resize event to detect whenever the screen resizes and updates accordingly.
Then we return a conditional header based on whether this is true or false.
And it works!

However, now imagine we also want to use this logic in another component?
We could copy-paste this code there and be done with it. Yes, correct, but it's not a neat solution as we would be re-using code that we could extract.
And that's precisely where custom hooks can come in handy.
Let's take what we just created, but place it inside a custom hook now.
Create the hook file called useMedia inside a hooks directory (You must create this directory).
Pay attention to the use part as this is a condition to hooks. They MUST start with use.
import { useEffect, useState } from 'react';
const useMedia = () => {
const [isDesktop, setIsDesktop] = useState(false);
useEffect(() => {
const media = window.matchMedia('(min-width: 960px)');
const listener = () => setIsDesktop(media.matches);
listener();
window.addEventListener('resize', listener);
return () => window.removeEventListener('resize', listener);
}, [isDesktop]);
return isDesktop;
};
export default useMedia;
We can now go back to our App.js and clean up the code to look more like this.
import useMedia from './hooks/useMedia';
function App() {
const isDesktop = useMedia();
return (
<div className='App'>
{isDesktop ? <h1>Desktop</h1> : <h1>Small screen</h1>}
</div>
);
}
export default App;
Pretty solid. We now have a hook that we can re-use in multiple components.
However, we can go a step further and extract the actual media query from it. That way, we can test for multiple media queries using the same hook.
First, make sure the useMedia hook can accept a query parameter.
const useMedia = (query) => {
Then we need to modify our variables inside this hook and use the query to test against.
const useMedia = (query) => {
const [matches, setMatches] = useState(false);
useEffect(() => {
const media = window.matchMedia(query);
const listener = () => setMatches(media.matches);
listener();
media.addEventListener('change', listener);
return () => media.removeEventListener('change', listener);
}, [matches, query]);
return matches;
};
Now we can modify our use to look like this:
const isDesktop = useMedia('(min-width: 960px)');
Pretty solid, right! You can even go ahead and use this to check for multiple media queries now.
Do note we also changed the listener, so instead of adding a resize listener, we add one to the media query to see if that changes!
This stuff super stokes me as it becomes way easier to split out our codebase with custom hooks.
You can find this complete example on GitHub.
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter