Reputation: 732
I have different pages of content to apply, depending on the width of the window. I did it with the condition:
{window.innerWidth > 575 ? <Big component> : <Little component>}
It works well but not if you reduce the page without reload manually.
I tried to implements useEffect on it, but I don't find how to trigger it by the widow.width change. Please, could you help me ?
This is my code:
const [windowSize, setWindowSize] = useState(window.innerWidth);
useEffect(() => {
setWindowSize(window.innerWidth);
});
return (
...
{windowSize > 575 ? <Big component> : <Little component>}
...
);
Thanks in advance.
Upvotes: 2
Views: 4814
Reputation: 457
Using Utsav and MDN docs for debouncing this is my custom hook I created for my application
import { useCallback, useEffect, useState } from "react";
interface MediaObj {
isMobile: boolean;
isTablet: boolean;
isLaptop: boolean;
isDesktop: boolean;
};
//upper limit (BP) for each device. based off Bootstrap breakpoints
const mobileBP = 768; //sm
const tabletBP = 992; //md
const laptopBP = 1200 //lg
const dekstopBP = 1400; //xlg
const defaultValues= {
isMobile: window.innerWidth <= mobileBP ? true : false,
isTablet: (window.innerWidth > mobileBP && window.innerWidth <= tabletBP) ? true : false,
isLaptop: (window.innerWidth > tabletBP && window.innerWidth <= laptopBP) ? true : false,
isDesktop: (window.innerWidth > laptopBP) ? true : false,
}
const useDetectResize = () => {
const [mediaType, setMediaType] = useState<MediaObj>(defaultValues);
const [windowDimensions, setWindowDimensions] = useState<{w:number, h:number}>({w: window.innerWidth, h: window.innerHeight});
let timeout = false;
//register callback so our function isn't instantiated on each re-render.
const handleWindowResize = useCallback((timeout:any) => {
// clear the timeout
clearTimeout(timeout);
// debounce getDimensions function ever N ms
timeout = setTimeout(getDimensions, 1000);
//store width/height and mediaState
function getDimensions(){
let width = window.innerWidth;
let height = window.innerHeight;
setWindowDimensions(currState => currState = {w:width, h: height});
console.log('Width Saved!: ', width);
let mediaTypes = {
isMobile: window.innerWidth <= mobileBP ? true : false,
isTablet: (window.innerWidth > mobileBP && window.innerWidth <= tabletBP) ? true : false,
isLaptop: (window.innerWidth > tabletBP && window.innerWidth <= laptopBP) ? true : false,
isDesktop: (window.innerWidth > laptopBP) ? true : false,
}
setMediaType(currState => currState = mediaTypes);
}
}, []);
useEffect(() => {
// window.resize event listener will call handleWindowResize() whenever screen width is adjusted.
window.addEventListener('resize', handleWindowResize);
return () => window.removeEventListener('resize', handleWindowResize);
},[])
return{
isMobile: mediaType.isMobile,
isTablet: mediaType.isTablet,
isLaptop: mediaType.isLaptop,
isDesktop: mediaType.isDesktop,
windowDimensions
}
}
export default useDetectResize;
then I call it in my components like so
const { windowDimensions, isMobile, isTablet, isLaptop, isDesktop } = useDetectResize();
Upvotes: 0
Reputation: 2889
Without registering an event listener, you cannot achieve updating of innerWidth
.
Here is a way to do this with an event listener:
const handleWindowResize = useCallback(event => {
setWindowSize(window.innerWidth);
}, []);
useEffect(() => {
window.addEventListener('resize', handleWindowResize);
return () => {
window.removeEventListener('resize', handleWindowResize);
};
}, [handleWindowResize]);
You can add and remove the event in the useEffect
hook.
You can create a memoized
handler using useCallback
hook.
This is called a callback pattern
.
In this we are simply setting the state using the updated innerWidth, its best to use the callback pattern
and register the event listeners only on initial mount.
If you register the events in useEffect
hook, the listeners reference along with its lexical scope is being used by the event listener but a new function is created with updated closure on each new render preventing the handler from accessing the updated state.
More on useCallback
here and here.
Upvotes: 6
Reputation: 4330
You can use window.onresize
You can check here.
function reportWindowSize() {
console.log(window.innerHeight, window.innerWidth)
}
window.onresize = reportWindowSize;
For your use case. You can use window.addEventListener('resize', reportWindowSize)
inside your hook.
const [windowWidth, setWindowWidth] = useState(window.innerWidth)
useEffect(() => {
function reportWindowSize() {
setWindowWidth(window.innerWidth)
console.log(window.innerHeight, window.innerWidth)
}
// Trigger this function on resize
window.addEventListener('resize', reportWindowSize)
// Cleanup for componentWillUnmount
return () => window.removeEventListener('resize', reportWindowSize)
}, [])
Upvotes: 3