I don’t know if I’m miss understanding what useCallBack is for or if I’m doing something wrong.
I have a hook to determine whether I’m in desktop, tablet or mobile which listens to the width of the screen. This works fine.
Now I have a component where I need to calculate the height of something, I need to do this only in mobile and I’m listening to the window resize in case some text breaks and the height increases.
But in my handleWindowSizeChange
function in my component isDesktop is always with the initial value. Even using useCallBack
hook.
Here’s my code:
const handleWindowSizeChange = useCallback(() => {
console.log(isDesktop) // if I start in desktop it's always true, if I start in mobile it's always false.
}, [isDesktop]);
useEffect(() => {
console.log(isDesktop) //This triggers when you hit the breakpoint with the correct value
}, [isDesktop]);
useEffect(() => {
if (!loaded) return;
handleWindowSizeChange();
window.addEventListener('resize', handleWindowSizeChange);
return () => {
window.removeEventListener('resize', handleWindowSizeChange);
}
}, [loaded]);
Here is my hook (ugly but works):
'use client'
import { useState, useEffect } from 'react';
import { throttle } from './utils';
export default function useDeviceDetect() {
const [isMobile, setMobile] = useState(false);
const [isTablet, setTablet] = useState(false);
const [isDesktop, setDesktop] = useState(false);
const [loaded, setLoaded] = useState(false);
const breakpointSmall = 480;
const breakpointMedium = 960;
const breakpointLarge = 1280;
const breakpointXLarge = 1680;
function handleWindowSizeChange() {
if (window.screen.width < breakpointSmall) {
setMobile(true);
setTablet(false);
setDesktop(false);
}
if (window.screen.width >= breakpointSmall && window.screen.width < breakpointMedium) {
setMobile(false);
setTablet(true);
setDesktop(false);
}
if (window.screen.width >= breakpointMedium && window.screen.width < breakpointLarge) {
setMobile(false);
setTablet(false);
setDesktop(true);
}
if (window.screen.width >= breakpointLarge && window.screen.width < breakpointXLarge) {
setMobile(false);
setTablet(false);
setDesktop(true);
}
if (window.screen.width >= breakpointXLarge) {
setMobile(false);
setTablet(false);
setDesktop(true);
}
setLoaded(true);
}
useEffect(() => {
handleWindowSizeChange();
window.addEventListener('resize', handleWindowSizeChange);
return () => {
window.removeEventListener('resize', handleWindowSizeChange);
}
}, []);
return { isMobile, isTablet, isDesktop, loaded };
}
If I understood correctly the useCallBack
caches the function and rebuild’s it when the dependency changes.
I thought this hook is to “fix the bug” where state isn’t updated in a setInterval
for example.
Am I doing something wrong or did I miss understand this hook?
3
use window.innerWidth
in place of window.screen.width
.
"use client"
import { useState, useEffect, useCallback } from "react"
export default function useDeviceDetect() {
const [device, setDevice] = useState({
isMobile: false,
isTablet: false,
isDesktop: false,
loaded: false,
})
const breakpoints = { small: 480, medium: 960, large: 1280, xLarge: 1680 }
const handleWindowSizeChange = useCallback(() => {
// Use window.innerWidth (not window.screen.width)
const width = window.innerWidth
setDevice({
isMobile: width < breakpoints.small,
isTablet: width >= breakpoints.small && width < breakpoints.medium,
isDesktop: width >= breakpoints.medium,
loaded: true,
})
}, [breakpoints.medium, breakpoints.small])
useEffect(() => {
handleWindowSizeChange()
window.addEventListener("resize", handleWindowSizeChange)
return () => window.removeEventListener("resize", handleWindowSizeChange)
}, [handleWindowSizeChange])
return device
}
page.tsx
"use client"
import useDeviceDetect from "@/hooks/useDeviceDetect"
function Page() {
const { isDesktop, isMobile, isTablet, loaded } = useDeviceDetect()
if (!loaded) return <div>Loading...</div> // Show a loading state until device detection is complete
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h1>Device Detection</h1>
{isMobile && <h2>You are on a Mobile device!</h2>}
{isTablet && <h2>You are on a Tablet device!</h2>}
{isDesktop && <h2>You are on a Desktop device!</h2>}
<p>Resize the window to see the UI update.</p>
</div>
)
}
export default Page