hensei-web/hooks/useLockedBody.tsx

56 lines
1.5 KiB
TypeScript

// https://usehooks-ts.com/react-hook/use-locked-body
import { useEffect, useState } from 'react'
import { useIsomorphicLayoutEffect } from 'usehooks-ts'
type UseLockedBodyOutput = [boolean, (locked: boolean) => void]
function useLockedBody(
initialLocked = false,
rootId = '___gatsby' // Default to `___gatsby` to not introduce breaking change
): UseLockedBodyOutput {
const [locked, setLocked] = useState(initialLocked)
// Do the side effect before render
useIsomorphicLayoutEffect(() => {
if (!locked) {
return
}
// Save initial body style
const originalOverflow = document.body.style.overflow
const originalPaddingRight = document.body.style.paddingRight
// Lock body scroll
document.body.style.overflow = 'hidden'
// Get the scrollBar width
const root = document.getElementById(rootId) // or root
const scrollBarWidth = root ? root.offsetWidth - root.scrollWidth : 0
// Avoid width reflow
if (scrollBarWidth) {
document.body.style.paddingRight = `${scrollBarWidth}px`
}
return () => {
document.body.style.overflow = originalOverflow
if (scrollBarWidth) {
document.body.style.paddingRight = originalPaddingRight
}
}
}, [locked])
// Update state if initialValue changes
useEffect(() => {
if (locked !== initialLocked) {
setLocked(initialLocked)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialLocked])
return [locked, setLocked]
}
export default useLockedBody