-
-
Save gaearon/cb5add26336003ed8c0004c4ba820eae to your computer and use it in GitHub Desktop.
function MyResponsiveComponent() { | |
const width = useWindowWidth(); // Our custom Hook | |
return ( | |
<p>Window width is {width}</p> | |
); | |
} |
function useWindowWidth() { | |
const [width, setWidth] = useState(window.innerWidth); | |
useEffect(() => { | |
const handleResize = () => setWidth(window.innerWidth); | |
window.addEventListener('resize', handleResize); | |
return () => { | |
window.removeEventListener('resize', handleResize); | |
}; | |
}); | |
return width; | |
} |
Fun fact; this works on codesandbox:
function useWindowWidth() { const [width, setWidth] = useState(window.innerWidth); // const handleResize = () => setWidth(window.innerWidth); // window.addEventListener("resize", handleResize); useEffect(() => { setWidth(window.innerWidth); return () => { // window.removeEventListener("resize", handleResize); }; }); return width; }
I'm guessing that is because of the
window.innerWidth
reference.
https://codesandbox.io/s/rm3v532lwm
It's because the effect runs on every render, and within the effect you are updating the local state, which causes a subsequent re-render. It's a side-effect that causes a render, kind of a self-invoking hook :)
In fact this is an important point, as it is easy to mistakenly abuse Effect hooks if you aren't clear on how they (currently) work. As mentioned in other comments here, sticking in an empty array as the Effect trigger will give us the expected behaviour.
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); <-- empty array: effect runs only on mount, cleans up on unmount
It is a somewhat contentious issue, that updating a local state value always triggers a re-render even if the value you are setting is identical to the previous value. The useState
hook is mimicking the behaviour of a class Component in which shouldComponentUpdate
returns true as its default implementation. We can extend PureComponent if we want a smarter updating class component, and it would be trivial to implement a usePureState
hook to achieve the same for a functional component. Personally, I think it would have been nice to take advantage of having a brand-new API to make "pure" the default behaviour in hooks, but I fully understand the reasons why the React team have not done that; I had just hoped they would provide a usePureState
hook out-of-the-box.
How would one go about testing these event listeners?
Hi @gaearon, I'm very new to hooks, but isn't this typically the case where you would opt out from cleaning at each render in
useEffect
and rather have:useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); // <-- empty arrayAs suggested in the note from the docs, the empty array makes it so that the effect is run and cleaned up when the component mounts / unmounts.
Otherwise you would add and remove a window listener at each render, right?
I had same thought, I wonder why Dan uses his original example though (useEffect
without []
) - it can be slightly confusing. @gaearon
i am one of the newbie in react learning.
thank you for explanation.
This cool example just demonstrated everything I wanted to know about Hooks. Thank you!!!!