From 0754bce76e15e1c5ce8b272358d7570c06e99277 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sun, 24 Mar 2019 10:27:38 +1100 Subject: [PATCH 1/3] useLockBodyScroll --- docs/useLockBodyScroll.md | 31 +++++++++++++++++++++ src/__stories__/useLockBodyScroll.story.tsx | 24 ++++++++++++++++ src/index.ts | 2 ++ src/useLockBodyScroll.ts | 19 +++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 docs/useLockBodyScroll.md create mode 100644 src/__stories__/useLockBodyScroll.story.tsx create mode 100644 src/useLockBodyScroll.ts diff --git a/docs/useLockBodyScroll.md b/docs/useLockBodyScroll.md new file mode 100644 index 0000000000..d0db437331 --- /dev/null +++ b/docs/useLockBodyScroll.md @@ -0,0 +1,31 @@ +# `useLockBodyScroll` + +React side-effect hook that locks scrolling on the body element. Useful for modal and other overlay components. + +## Usage + +```jsx +import {useLockBodyScroll, useToggle} from 'react-use'; + +const Demo = () => { + const [locked, toggleLocked] = useToggle(false) + + useLockBodyScroll(locked); + + return ( +
+ +
+ ); +}; +``` + +## Reference + +```ts +useToggle(enabled?: boolean = true); +``` + +- `enabled` — Hook will lock scrolling on the body element if `true`, defaults to `true` diff --git a/src/__stories__/useLockBodyScroll.story.tsx b/src/__stories__/useLockBodyScroll.story.tsx new file mode 100644 index 0000000000..2c14ff5e83 --- /dev/null +++ b/src/__stories__/useLockBodyScroll.story.tsx @@ -0,0 +1,24 @@ +import {storiesOf} from '@storybook/react'; +import * as React from 'react'; +import {useLockBodyScroll, useToggle} from '..'; +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + const [locked, toggleLocked] = useToggle(false) + + useLockBodyScroll(locked); + + return ( +
+ +
+ ); +}; + +storiesOf('Side effects|useLockBodyScroll', module) + .add('Docs', () => ) + .add('Demo', () => + + ) diff --git a/src/index.ts b/src/index.ts index ba0298d64d..34047e79a7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ import useLifecycles from './useLifecycles'; import useList from './useList'; import useLocalStorage from './useLocalStorage'; import useLocation from './useLocation'; +import useLockBodyScroll from './useLockBodyScroll'; import useLogger from './useLogger'; import useMap from './useMap'; import useMedia from './useMedia'; @@ -77,6 +78,7 @@ export { useList, useLocalStorage, useLocation, + useLockBodyScroll, useLogger, useMap, useMedia, diff --git a/src/useLockBodyScroll.ts b/src/useLockBodyScroll.ts new file mode 100644 index 0000000000..d0d2023198 --- /dev/null +++ b/src/useLockBodyScroll.ts @@ -0,0 +1,19 @@ +import {useRef, useEffect} from 'react'; +import {isClient} from './util'; +import useUnmount from './useUnmount'; + +const useLockBodyScroll = (enabled: boolean = true) => { + const originalOverflow = useRef( + isClient ? window.getComputedStyle(document.body).overflow : 'visible' + ); + + useEffect(() => { + document.body.style.overflow = enabled ? "hidden" : originalOverflow.current; + }, [enabled]); + + useUnmount(() => { + document.body.style.overflow = originalOverflow.current + }); +} + +export default useLockBodyScroll; From fa6e9f3dcebc0afce5db595a5e3a15b8a7a5b065 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sun, 24 Mar 2019 10:36:36 +1100 Subject: [PATCH 2/3] Added useLockBodyScroll to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d390cd73a6..8b5c40e674 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ - [`useCss`](./docs/useCss.md) — dynamically adjusts CSS. - [`useFavicon`](./docs/useFavicon.md) — sets favicon of the page. - [`useLocalStorage`](./docs/useLocalStorage.md) — manages a value in `localStorage`. + - [`useLockBodyScroll`](./docs/useLockBodyScroll.md) — lock scrolling of the body element. - [`useSessionStorage`](./docs/useSessionStorage.md) — manages a value in `sessionStorage`. - [`useTitle`](./docs/useTitle.md) — sets title of the page. - [`useDebounce`](./docs/useDebounce.md) — debounces a function. From 9bb7047b7395aaba3da096697ef30aa46d188ec5 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 24 Mar 2019 17:43:35 +0100 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20keep=20global=20stat?= =?UTF-8?q?e=20of=20all=20useLockBodyScroll=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change will keep document body locked as long as at least one hook is enabled on the page. Maybe it makes sense to generalize this logic for other side-effect hooks. One such hook could be useBlurBody that blurs page - useful for modals and overlays. --- src/__stories__/useLockBodyScroll.story.tsx | 22 +++++++++--- src/useLockBodyScroll.ts | 37 +++++++++++++-------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/__stories__/useLockBodyScroll.story.tsx b/src/__stories__/useLockBodyScroll.story.tsx index 2c14ff5e83..b701d27d77 100644 --- a/src/__stories__/useLockBodyScroll.story.tsx +++ b/src/__stories__/useLockBodyScroll.story.tsx @@ -5,20 +5,34 @@ import ShowDocs from '../util/ShowDocs'; const Demo = () => { const [locked, toggleLocked] = useToggle(false) - useLockBodyScroll(locked); return (
-
); }; +const AnotherComponent = () => { + const [locked, toggleLocked] = useToggle(false) + useLockBodyScroll(locked); + + return ( + + ); +}; + storiesOf('Side effects|useLockBodyScroll', module) .add('Docs', () => ) - .add('Demo', () => - + .add('Demo', () => ) + .add('Two hooks on page', () => + <> + + + ) diff --git a/src/useLockBodyScroll.ts b/src/useLockBodyScroll.ts index d0d2023198..67b2220659 100644 --- a/src/useLockBodyScroll.ts +++ b/src/useLockBodyScroll.ts @@ -1,19 +1,30 @@ -import {useRef, useEffect} from 'react'; -import {isClient} from './util'; -import useUnmount from './useUnmount'; +import {useEffect} from 'react'; -const useLockBodyScroll = (enabled: boolean = true) => { - const originalOverflow = useRef( - isClient ? window.getComputedStyle(document.body).overflow : 'visible' - ); +let counter = 0; +let originalOverflow: string | null = null; + +const lock = () => { + originalOverflow = window.getComputedStyle(document.body).overflow; + document.body.style.overflow = 'hidden'; +}; + +const unlock = () => { + document.body.style.overflow = originalOverflow; + originalOverflow = null; +}; - useEffect(() => { - document.body.style.overflow = enabled ? "hidden" : originalOverflow.current; - }, [enabled]); +const increment = () => { + counter++; + if (counter === 1) lock(); +}; - useUnmount(() => { - document.body.style.overflow = originalOverflow.current - }); +const decrement = () => { + counter--; + if (counter === 0) unlock(); +}; + +const useLockBodyScroll = (enabled: boolean = true) => { + useEffect(() => enabled ? (increment(), decrement) : undefined, [enabled]); } export default useLockBodyScroll;