useLockedBody()
This React hook is used to block the scrolling of the page.
A good example of a use case is when you need to open a modal.
For flexibility, this hook offers 2 APIs:
- Use it as we would use a useState (example 1)
- Use it with our own logic, coming from a props or redux for example (example 2)
Finally, you can optionally change the reflow padding if you have another sidebar size than the default (15px)
The Hook
1import { useEffect, useState } from 'react'23// See: https://usehooks-ts.com/react-hook/use-isomorphic-layout-effect4import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect'56type ReturnType = [boolean, (locked: boolean) => void]78function useLockedBody(initialLocked = false): ReturnType {9 const [locked, setLocked] = useState(initialLocked)1011 // Do the side effect before render12 useIsomorphicLayoutEffect(() => {13 if (!locked) {14 return15 }1617 // Save initial body style18 const originalOverflow = document.body.style.overflow19 const originalPaddingRight = document.body.style.paddingRight2021 // Lock body scroll22 document.body.style.overflow = 'hidden'2324 // Get the scrollBar width25 const root = document.getElementById('___gatsby') // or root26 const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 02728 // Avoid width reflow29 if (scrollBarWidth) {30 document.body.style.paddingRight = `${scrollBarWidth}px`31 }3233 return () => {34 document.body.style.overflow = originalOverflow3536 if (scrollBarWidth) {37 document.body.style.paddingRight = originalPaddingRight38 }39 }40 }, [locked])4142 // Update state if initialValue changes43 useEffect(() => {44 if (locked !== initialLocked) {45 setLocked(initialLocked)46 }47 // eslint-disable-next-line react-hooks/exhaustive-deps48 }, [initialLocked])4950 return [locked, setLocked]51}5253export default useLockedBody
Usage
1import React, { CSSProperties, useState } from 'react'23import { useLockedBody } from 'usehooks-ts'45const fixedCenterStyle: CSSProperties = {6 position: 'fixed',7 top: '50%',8 left: '50%',9 transform: 'translate(-50%, -50%)',10}1112const fakeScrollableStyle: CSSProperties = {13 minHeight: '150vh',14 background: 'linear-gradient(palegreen, palegoldenrod, palevioletred)',15}1617// Example 1: useLockedBody as useState()18export default function App() {19 const [locked, setLocked] = useLockedBody()2021 const toggleLocked = () => {22 setLocked(!locked)23 }2425 return (26 <div style={fakeScrollableStyle}>27 <button style={fixedCenterStyle} onClick={toggleLocked}>28 {locked ? 'unlock scroll' : 'lock scroll'}29 </button>30 </div>31 )32}3334// Example 2: useLockedBody with our custom state35export function App2() {36 const [locked, setLocked] = useState(false)3738 const toggleLocked = () => {39 setLocked(!locked)40 }4142 useLockedBody(locked)4344 return (45 <div style={fakeScrollableStyle}>46 <button style={fixedCenterStyle} onClick={toggleLocked}>47 {locked ? 'unlock scroll' : 'lock scroll'}48 </button>49 </div>50 )51}
See a way to make this page better?
Edit there »