I have a component that needs to listen to window resize, and hide/show sidebar appropriately. I decided to create a custom hook for this purpose, and it doesn't seem to work properly.
The main problem is that the mainSidebar
value is always constant, and the update is not being read properly.
This is the hook itself:
export function useResizeListener(cb: (this: Window, e: UIEvent) => any) {
const [size, setSize] = React.useState({ width: window.outerWidth, height: window.outerHeight })
React.useEffect(() => {
function _wrapped(this: Window, e: UIEvent): any {
console.debug('setting size and calling callback', { width: this.outerWidth, height: this.outerHeight })
setSize({ width: this.outerWidth, height: this.outerHeight })
cb.call(this, e)
}
window.addEventListener('resize', _wrapped)
return () => window.removeEventListener('resize', _wrapped)
}, [size.width, size.height])
}
And this is the component using it:
const shouldHaveSidebar = () => document.body.clientWidth >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const [mainSidebar, toggleMainSidebar] = React.useState(shouldHaveSidebar())
useResizeListener(() => {
console.debug('device width vs tablet width:', document.body.clientWidth, '>=', ScreenSizes.Tablet)
console.debug('shouldHaveSidebar vs mainSidebar', shouldHaveSidebar(), '!==', mainSidebar)
if (shouldHaveSidebar() !== mainSidebar) {
console.debug('setting to', shouldHaveSidebar())
toggleMainSidebar(shouldHaveSidebar())
}
})
return ...
}
Upon toggling viewport size on the Chrome dev inspector device modes, I get this output in the console. Note I started with desktop and then changed to mobile L.
setting size and calling callback {width: 1680, height: 1027}
device width vs tablet width: 1146 >= 800
shouldHaveSidebar vs mainSidebar true !== false
setting to true
setting size and calling callback {width: 425, height: 779}
device width vs tablet width: 425 >= 800
shouldHaveSidebar vs mainSidebar false !== false // <--- should be: false !== true
_wrapper
function between outside and inside the useEffect
hookNeither of these worked.
What am I doing wrong?
I would suggest restructuring the solution. First I would change the useResizeListener
into a hook that gives you the window size:
export function useWindowSize() {
const [size, setSize] = React.useState({ width: window.clientWidth, height: window.clientHeight });
React.useEffect(() => {
function handleResize(e: UIEvent) {
setSize({ width: window.clientWidth, height: window.clientHeight });
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
Then in your HomePage
component:
const shouldHaveSidebar = (size) => size.width >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const size = useWindowSize();
const mainSidebar = shouldHaveSidebar(size);
// Now if you want to perform an action when the size changes you cna simply do:
useEffect(() => {
// Perform your action here
}, [size.width, size.height]);
// ...
}