import { useState, useEffect } from 'react'; | |
// Usage | |
function App() { | |
const size = useWindowSize(); | |
return ( | |
<div> | |
{size.width}px / {size.height}px | |
</div> | |
); | |
} | |
// Hook | |
function useWindowSize() { | |
// Initialize state with undefined width/height so server and client renders match | |
// Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/ | |
const [windowSize, setWindowSize] = useState({ | |
width: undefined, | |
height: undefined, | |
}); | |
useEffect(() => { | |
// Handler to call on window resize | |
function handleResize() { | |
// Set window width/height to state | |
setWindowSize({ | |
width: window.innerWidth, | |
height: window.innerHeight, | |
}); | |
} | |
// Add event listener | |
window.addEventListener("resize", handleResize); | |
// Call handler right away so state gets updated with initial window size | |
handleResize(); | |
// Remove event listener on cleanup | |
return () => window.removeEventListener("resize", handleResize); | |
}, []); // Empty array ensures that effect is only run on mount | |
return windowSize; | |
} |
This comment has been minimized.
This comment has been minimized.
@mattfysh Good catch! Will update shortly. |
This comment has been minimized.
This comment has been minimized.
Couldn't we declare Also, the import { useState, useEffect } from 'react';
// Usage
function App() {
const [width, height] = useWindowSize();
return (
<div>
{width}px / {height}px
</div>
);
}
// Helpers
const IS_CLIENT = typeof window === 'object';
const getSize = IS_CLIENT ?
() => [window.innerWidth, window.innerHeight] :
() => [undefined, undefined];
// Hook
function useWindowSize() {
const [windowSize, setWindowSize] = useState(getSize);
useEffect(() => {
if (!IS_CLIENT) {
return;
}
function handleResize() {
setWindowSize(getSize());
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount and unmount
return windowSize;
} |
This comment has been minimized.
This comment has been minimized.
@AndyBarron Thanks, fixed the issue with |
This comment has been minimized.
This comment has been minimized.
Given that |
This comment has been minimized.
This comment has been minimized.
I suggest moving |
This comment has been minimized.
This comment has been minimized.
An optional debounce value would fit nicely here ;) |
This comment has been minimized.
This comment has been minimized.
@ianobermiller done! |
This comment has been minimized.
This comment has been minimized.
@gragland Thanks for this hook, I'm using it on a project! |
This comment has been minimized.
This comment has been minimized.
This version uses an array as result (because it's just better for destructuring) and has more options with smaller footprint. import { useState, useEffect } from 'react';
type Result = [number | undefined, number | undefined];
/**
* Returns current window size and updates state if window if resized
* @param element Optional element to use for accessing the window object instead of global window variable
* @param disableResizeEvent Disable the resize event and only get initial window size
* @return [width, height]
*/
export default function useWindowSize(element?: HTMLElement | null, disableResizeEvent?: boolean): Result {
// If we are not even in a browser environment, do not even useEffect - window size will always be undefined
if (typeof window !== 'object')
return [undefined, undefined];
// Get reference to window objecz
const wnd = element && element.ownerDocument && element.ownerDocument.defaultView || window;
// Get / initialize state
const [result, setSize] = useState<Result>([wnd && wnd.innerWidth, wnd && wnd.innerHeight]);
useEffect(() => {
if (typeof wnd === 'object') {
const handleResize = () => setSize([wnd.innerWidth, wnd.innerHeight]);
handleResize();
if (!disableResizeEvent) {
wnd.addEventListener('resize', handleResize);
return () => wnd.removeEventListener('resize', handleResize);
}
}
}, [wnd, disableResizeEvent]);
return result;
} |
This comment has been minimized.
This comment has been minimized.
When using typescript and
this leads to returning |
This comment has been minimized.
This comment has been minimized.
Any reason why you're not debouncing or throttling this hook? |
This comment has been minimized.
This comment has been minimized.
Here you go:
|
This comment has been minimized.
This comment has been minimized.
Thanks for the reusable hook, it helped me a lot. As going head i thought of writing the test case for the useWindowSize hook. so i was able to cover 90% but since i was also new to unit testing. anyone would have a fair knowledge how can we have 100% code coverage. so the people won't need to write it again and again for the test case. Also please suggest any better way, i was using enzyme and jest but since hooks support was less i was using react testing library. @testing-library/react so using act how can i make the window to a different data type rather than object so it will have 100% test coverage. //hooks.js
// hooks.test.js
|
This comment has been minimized.
This comment has been minimized.
When I use this hook twice, it adds Event Listener to window twice. Should this be adding only once and Event caching needed ? |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
Hello! 👋🏻 This code has an issue with server rendering. I ran in to a similar issue here: gatsbyjs/gatsby#14601 The underlying issue is in return {
width: isClient ? window.innerWidth : undefined,
height: isClient ? window.innerHeight : undefined
}; The problem is that the server render value of To fix this, I modified the hook to always initialize the values to export default function useWindowSize() {
// initialize to undefined so the server render and first client render match
// https://github.com/gatsbyjs/gatsby/issues/14601
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener("resize", handleResize);
handleResize();
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowSize;
} |
This comment has been minimized.
You could pass a function into
useState
, so the initial state is calculated only once (and not on each render), i.e: