import { useState, useLayoutEffect } from 'react'; | |
// Usage | |
function App(){ | |
// State for our modal | |
const [modalOpen, setModalOpen] = useState(false); | |
return ( | |
<div> | |
<button onClick={() => setModalOpen(true)}>Show Modal</button> | |
<Content /> | |
{modalOpen && ( | |
<Modal | |
title="Try scrolling" | |
content="I bet you you can't! Muahahaha 😈" | |
onClose={() => setModalOpen(false)} | |
/> | |
)} | |
</div> | |
); | |
} | |
function Modal({ title, content, onClose }){ | |
// Call hook to lock body scroll | |
useLockBodyScroll(); | |
return ( | |
<div className="modal-overlay" onClick={onClose}> | |
<div className="modal"> | |
<h2>{title}</h2> | |
<p>{content}</p> | |
</div> | |
</div> | |
); | |
} | |
// Hook | |
function useLockBodyScroll() { | |
useLayoutEffect(() => { | |
// Get original value of body overflow | |
const originalStyle = window.getComputedStyle(document.body).overflow; | |
// Prevent scrolling on mount | |
document.body.style.overflow = 'hidden'; | |
// Re-enable scrolling when component unmounts | |
return () => document.body.style.overflow = originalStyle; | |
}, []); // Empty array ensures effect is only run on mount and unmount | |
} |
This comment has been minimized.
This comment has been minimized.
I agree, setting |
This comment has been minimized.
This comment has been minimized.
useLayoutEffect(() => {
// Prevent scrolling on mount
document.body.style.overflow = 'hidden';
// Re-enable scrolling when component unmounts
return () => (document.body.style.overflow = 'visible');
}, []); // Empty array ensures effect is only run on mount and unmount
Writing these with hooks is great, which decreases the code lines. |
This comment has been minimized.
This comment has been minimized.
@sstur Good point! If the body has a background color then they may still have a white area where the scrollbar was.. but I supposed that is better than actual body content moving. |
This comment has been minimized.
This comment has been minimized.
@anyexinglu Happy to accept a pull request (we're now open source at https://github.com/gragland/usehooks) or look at a code example in this gist. I supposed we'd just have to grab the current |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@solutweb Ah good point. When I have some time will have to dig into what's going on there. Open to pull requests if you happen to figure it out! |
This comment has been minimized.
This comment has been minimized.
in addition to width issue @anyexinglu pointed out about windows scrollbars, there is also an issue with with fixed backgrounds resizing when you overflow:hidden the scrollbars. material-ui tries to resolve this by adding padding to any element with the class mui-fixed. |
This comment has been minimized.
This comment has been minimized.
Why does it use |
This comment has been minimized.
This comment has been minimized.
When using it in TypeScript, it complains that the first param doesn't match Changing |
This comment has been minimized.
This comment has been minimized.
This currently won't work for iOS devices. You could rewrite it like this
|
This comment has been minimized.
This comment has been minimized.
I took a shot at modifying this to make it work:
It seems to be working with my changes in my sandbox fork here: https://codesandbox.io/s/uselockbodyscroll-example-mq9rm. But I'm pretty new to custom hooks (and programming, really) so I'm open to suggestions/critique 👍🏼 |
This comment has been minimized.
It's worth noting that browsers / operating systems that use a non-hiding scrollbar (I think most versions of Windows) will show/hide the scrollbar when you change the body overflow from hidden to visible.
It's a tricky one to work around, but in the past I had some luck with first measuring the width/height of the body and then fixing that size before changing the overflow. That prevents the change of layout when you enable/disable scrolling.