Skip to content

Commit a92e9b2

Browse files
authored
feat: add useScroll hook
2 parents e49a507 + 584bd7e commit a92e9b2

File tree

6 files changed

+113
-5
lines changed

6 files changed

+113
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
- [`useMotion`](./docs/useMotion.md) — tracks state of device's motion sensor.
4545
- [`useNetwork`](./docs/useNetwork.md) — tracks state of user's internet connection.
4646
- [`useOrientation`](./docs/useOrientation.md) — tracks state of device's screen orientation.
47+
- [`useScroll`](./docs/useScroll.md) — some HTML element's scroll position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usescroll--docs)
48+
4749
- [`useSize`](./docs/useSize.md) — tracks some HTML element's dimensions.
4850
- [`useWindowScroll`](./docs/useWindowScroll.md) — tracks `Window` scroll position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usewindowscroll--docs)
4951
- [`useWindowSize`](./docs/useWindowSize.md) — tracks `Window` dimensions. [![][img-demo]](https://codesandbox.io/s/m7ln22668)

docs/useScroll.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# `useScroll`
2+
3+
React sensor hook that re-renders on when scroll position in a DOM element changes.
4+
5+
## Usage
6+
7+
```jsx
8+
import {useScroll} from 'react-use';
9+
10+
const Demo = () => {
11+
const element = React.useRef(null);
12+
const {x, y} = useScroll(element);
13+
14+
return (
15+
<div ref={element}>
16+
<div>x: {x}</div>
17+
<div>y: {y}</div>
18+
</div>
19+
);
20+
};
21+
```

src/__stories__/useScroll.story.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react';
2+
import {storiesOf} from '@storybook/react';
3+
import {useScroll} from '..';
4+
import ShowDocs from '../util/ShowDocs';
5+
6+
const Demo = () => {
7+
const element = React.useRef(null);
8+
const {x, y} = useScroll(element);
9+
10+
return (
11+
<>
12+
<div>x: {x}</div>
13+
<div>y: {y}</div>
14+
<div
15+
ref={element}
16+
style={{
17+
width: '400px',
18+
height: '400px',
19+
backgroundColor: 'whitesmoke',
20+
overflow: 'scroll'
21+
}}>
22+
23+
<div style={{width: '2000px', height: '2000px'}}>
24+
Scroll me
25+
</div>
26+
</div>
27+
</>
28+
);
29+
};
30+
31+
storiesOf('Sensors/useScroll', module)
32+
.add('Docs', () => <ShowDocs md={require('../../docs/useScroll.md')} />)
33+
.add('Demo', () =>
34+
<Demo/>
35+
)

src/__stories__/useWindowScroll.story.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ const Demo = () => {
88

99
return (
1010
<div style={{
11-
width: "200vw",
12-
height: "200vh"
11+
width: '200vw',
12+
height: '200vh'
1313
}}>
1414
<div style={{
15-
position: "fixed",
15+
position: 'fixed',
1616
left: 0,
17-
right: 0}}
18-
>
17+
right: 0
18+
}}>
1919
<div>x: {x}</div>
2020
<div>y: {y}</div>
2121
</div>

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import useOutsideClick from './useOutsideClick';
3535
import usePromise from './usePromise';
3636
import useRaf from './useRaf';
3737
import useRefMounted from './useRefMounted';
38+
import useScroll from './useScroll';
3839
import useSessionStorage from './useSessionStorage';
3940
import useSetState from './useSetState';
4041
import useSize from './useSize';
@@ -90,6 +91,7 @@ export {
9091
usePromise,
9192
useRaf,
9293
useRefMounted,
94+
useScroll,
9395
useSessionStorage,
9496
useSetState,
9597
useSize,

src/useScroll.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {useState, useEffect, useRef} from 'react';
2+
import {isClient} from './util';
3+
4+
export interface State {
5+
x: number;
6+
y: number;
7+
}
8+
9+
const useScroll = (ref): State => {
10+
const frame = useRef(0);
11+
const [state, setState] = useState<State>({
12+
x: isClient ? window.scrollX : 0,
13+
y: isClient ? window.scrollY : 0
14+
});
15+
16+
useEffect(() => {
17+
const handler = () => {
18+
19+
frame.current = requestAnimationFrame(() => {
20+
setState({
21+
x: ref.current.scrollLeft,
22+
y: ref.current.scrollTop
23+
});
24+
});
25+
}
26+
27+
if (ref && ref.current) {
28+
ref.current.addEventListener('scroll', handler, {
29+
capture: false,
30+
passive: true
31+
});
32+
}
33+
34+
return () => {
35+
if (frame.current) {
36+
cancelAnimationFrame(frame.current);
37+
}
38+
39+
if (ref && ref.current) {
40+
ref.current.removeEventListener('scroll', handler);
41+
}
42+
};
43+
}, [ref]);
44+
45+
return state;
46+
}
47+
48+
export default useScroll

0 commit comments

Comments
 (0)