From c75f997f8efe3bafb721c607bc68910ca2f59ce7 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sat, 23 Mar 2019 19:48:38 +1100 Subject: [PATCH 1/3] useScroll hook --- README.md | 2 ++ docs/useScroll.md | 21 ++++++++++++++ src/__stories__/useScroll.story.tsx | 35 ++++++++++++++++++++++ src/index.ts | 2 ++ src/useScroll.ts | 45 +++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+) create mode 100644 docs/useScroll.md create mode 100644 src/__stories__/useScroll.story.tsx create mode 100644 src/useScroll.ts diff --git a/README.md b/README.md index 569c82e26c..494daf12a1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ - [`useMotion`](./docs/useMotion.md) — tracks state of device's motion sensor. - [`useNetwork`](./docs/useNetwork.md) — tracks state of user's internet connection. - [`useOrientation`](./docs/useOrientation.md) — tracks state of device's screen orientation. + - [`useScroll`](./docs/useScroll.md) — some HTML element's scroll position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usescroll--docs) + - [`useSize`](./docs/useSize.md) — tracks some HTML element's dimensions. - [`useWindowScroll`](./docs/useWindowScroll.md) — tracks `Window` scroll position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usewindowscroll--docs) - [`useWindowSize`](./docs/useWindowSize.md) — tracks `Window` dimensions. [![][img-demo]](https://codesandbox.io/s/m7ln22668) diff --git a/docs/useScroll.md b/docs/useScroll.md new file mode 100644 index 0000000000..6de5730795 --- /dev/null +++ b/docs/useScroll.md @@ -0,0 +1,21 @@ +# `useScroll` + +React sensor hook that re-renders on when scroll position in a DOM element changes. + +## Usage + +```jsx +import {useScroll} from 'react-use'; + +const Demo = () => { + const element = React.useRef(null); + const {x, y} = useScroll(element); + + return ( +
+
x: {x}
+
y: {y}
+
+ ); +}; +``` diff --git a/src/__stories__/useScroll.story.tsx b/src/__stories__/useScroll.story.tsx new file mode 100644 index 0000000000..db0c2546fd --- /dev/null +++ b/src/__stories__/useScroll.story.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import {storiesOf} from '@storybook/react'; +import {useScroll} from '..'; +import ShowDocs from '../util/ShowDocs'; + +const Demo = () => { + const element = React.useRef(null); + const {x, y} = useScroll(element); + + return ( + <> +
x: {x}
+
y: {y}
+
+ +
+ Scroll me +
+
+ + ); +}; + +storiesOf('Sensors/useScroll', module) + .add('Docs', () => ) + .add('Demo', () => + + ) diff --git a/src/index.ts b/src/index.ts index 514af8dbdb..ba0298d64d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ import useOutsideClick from './useOutsideClick'; import usePromise from './usePromise'; import useRaf from './useRaf'; import useRefMounted from './useRefMounted'; +import useScroll from './useScroll'; import useSessionStorage from './useSessionStorage'; import useSetState from './useSetState'; import useSize from './useSize'; @@ -90,6 +91,7 @@ export { usePromise, useRaf, useRefMounted, + useScroll, useSessionStorage, useSetState, useSize, diff --git a/src/useScroll.ts b/src/useScroll.ts new file mode 100644 index 0000000000..3ebb4f5bde --- /dev/null +++ b/src/useScroll.ts @@ -0,0 +1,45 @@ +import {useState, useEffect, useRef} from 'react'; +import {isClient} from './util'; + +export interface State { + x: number; + y: number; +} + +const useScroll = (ref): State => { + const frame = useRef(0); + const [state, setState] = useState({ + x: isClient ? window.scrollX : 0, + y: isClient ? window.scrollY : 0 + }) + + useEffect(() => { + const handler = () => { + cancelAnimationFrame(frame.current) + + frame.current = requestAnimationFrame(() => { + setState({ + x: ref.current.scrollLeft, + y: ref.current.scrollTop + }) + }) + } + + if (ref && ref.current) { + ref.current.addEventListener('scroll', handler, { + capture: false, + passive: true + }) + } + + return () => { + if (ref && ref.current) { + ref.current.removeEventListener('scroll', handler) + } + }; + }, [ref]) + + return state +} + +export default useScroll From 71eb41695339af8a4f6154b04683165a7afdb2c2 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sat, 23 Mar 2019 19:56:49 +1100 Subject: [PATCH 2/3] useWindowScroll doc typos --- docs/useWindowScroll.md | 2 +- src/__stories__/useWindowScroll.story.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/useWindowScroll.md b/docs/useWindowScroll.md index 09d325d097..5e9884187f 100644 --- a/docs/useWindowScroll.md +++ b/docs/useWindowScroll.md @@ -1,4 +1,4 @@ -# `useWindowSize` +# `useWindowScroll` React sensor hook that re-renders on window scroll diff --git a/src/__stories__/useWindowScroll.story.tsx b/src/__stories__/useWindowScroll.story.tsx index 8b84475f1c..cce99a3ae9 100644 --- a/src/__stories__/useWindowScroll.story.tsx +++ b/src/__stories__/useWindowScroll.story.tsx @@ -8,14 +8,14 @@ const Demo = () => { return (
+ right: 0 + }}>
x: {x}
y: {y}
From 584bd7e618912fb5617bee33167f7e1b3ce6c176 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Sat, 23 Mar 2019 20:00:43 +1100 Subject: [PATCH 3/3] cancel animation frame on un-mount --- src/useScroll.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/useScroll.ts b/src/useScroll.ts index 3ebb4f5bde..dcbdc99834 100644 --- a/src/useScroll.ts +++ b/src/useScroll.ts @@ -11,35 +11,38 @@ const useScroll = (ref): State => { const [state, setState] = useState({ x: isClient ? window.scrollX : 0, y: isClient ? window.scrollY : 0 - }) + }); useEffect(() => { const handler = () => { - cancelAnimationFrame(frame.current) frame.current = requestAnimationFrame(() => { setState({ x: ref.current.scrollLeft, y: ref.current.scrollTop - }) - }) + }); + }); } if (ref && ref.current) { ref.current.addEventListener('scroll', handler, { capture: false, passive: true - }) + }); } return () => { + if (frame.current) { + cancelAnimationFrame(frame.current); + } + if (ref && ref.current) { - ref.current.removeEventListener('scroll', handler) + ref.current.removeEventListener('scroll', handler); } }; - }, [ref]) + }, [ref]); - return state + return state; } export default useScroll