Here is my code for the problem, my issue is that the event is triggering multiple times when it hits the 960px breakpoint. This should only fire once it reaches the breakpoint, but I am getting an almost exponential amount of event triggers.
const mediaQuery = '(max-width: 960px)';
const mediaQueryList = window.matchMedia(mediaQuery);
mediaQueryList.addEventListener('change', (event) => {
if (event.matches) {
setState((prevState) => ({
...prevState,
desktopNavActivated: false,
}));
} else {
setState((prevState) => ({
...prevState,
menuActivated: false,
navItemExpanded: false,
}));
}
});```
Hi there and welcome to StackOverflow!
I'm going to assume, that your code does not run within a useEffect
hook, which will result in a new event listener being added to your mediaQueryList
every time your component gets updated/rendered. This would explain the exponential amount of triggers. The useEffect
hook can be quite unintuitive at first, so I recommend reading up on it a bit. The docs do quite a good job at explaining the concept. I also found this article by Dan Abramov immensely helpful when I first started using effects.
The way you call your setState
function will always cause your component to update, no matter whether the current state already matches your media query, because you pass it a new object with every update. Unlike the setState
method of class based components (which compares object key/values iirc), the hook version only checks for strict equality when determining whether an update should trigger a re-render. An example:
{ foo: 'bar' } === { foo: 'bar' } // Always returns false. Try it in your console.
To prevent that from happening, you could set up a state hook that really only tracks the match result and derive your flags from it. Booleans work just fine with reference equality:
const [doesMatch, setDoesMatch] = useState(false)
So to put it all together:
const mediaQuery = '(max-width: 960px)';
const mediaQueryList = window.matchMedia(mediaQuery);
const MyComponent = () =>
const [match, updateMatch] = useState(false)
useEffect(() => {
const handleChange = (event) => {
updateMatch(event.matches)
}
mediaQueryList.addEventListener('change', handleChange)
return () => {
// This is called the cleanup phase aka beforeUnmount
mediaQueryList.removeEventListener('change', handleChange)
}
}, []) // Only do this once, aka hook-ish way of saying didMount
const desktopNavActivated = !match
// ...
// Further process your match result and return JSX or whatever
}
Again, I would really advise you to go over the React docs and some articles when you find the time. Hooks are awesome, but without understanding the underlying concepts they can become very frustrating to work with rather quickly.