Skip to content

Commit 17dfa8e

Browse files
authored
feat: add useMouse hook
2 parents 7b3e4d3 + 81cf157 commit 17dfa8e

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- [`useMedia`](./docs/useMedia.md) — tracks state of a CSS media query. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usemedia--demo)
4343
- [`useMediaDevices`](./docs/useMediaDevices.md) — tracks state of connected hardware devices.
4444
- [`useMotion`](./docs/useMotion.md) — tracks state of device's motion sensor.
45+
- [`useMouse`](./docs/useMouse.md) — tracks state of mouse position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usemouse--docs)
4546
- [`useNetwork`](./docs/useNetwork.md) — tracks state of user's internet connection.
4647
- [`useOrientation`](./docs/useOrientation.md) — tracks state of device's screen orientation.
4748
- [`useScroll`](./docs/useScroll.md) — tracks some HTML element's scroll position. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/sensors-usescroll--docs)

docs/useMouse.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# `useMouse`
2+
3+
React sensor hook that re-renders on mouse position changes.
4+
5+
## Usage
6+
7+
```jsx
8+
import {useMouse} from 'react-use';
9+
10+
const Demo = () => {
11+
const ref = React.useRef(null);
12+
const {docX, docY, posX, posY, elX, elY, elW, elH} = useScroll(ref);
13+
14+
return (
15+
<div ref={element}>
16+
<div>Mouse position in document - x:{docX} y:{docY}</div>
17+
<div>Mouse position in element - x:{posX} y:{posY}</div>
18+
<div>Element position - x:{elX} y:{elY}</div>
19+
<div>Element dimensions - {elW}x{elH}</div>
20+
</div>
21+
);
22+
};
23+
```

src/__stories__/useMouse.story.tsx

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

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import useMedia from './useMedia';
2828
import useMediaDevices from './useMediaDevices';
2929
import useMotion from './useMotion';
3030
import useMount from './useMount';
31+
import useMouse from './useMouse';
3132
import useNetwork from './useNetwork';
3233
import useNumber from './useNumber';
3334
import useObservable from './useObservable';
@@ -85,6 +86,7 @@ export {
8586
useMediaDevices,
8687
useMotion,
8788
useMount,
89+
useMouse,
8890
useNetwork,
8991
useNumber,
9092
useObservable,

src/useMouse.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {useState, useEffect, useRef, RefObject} from 'react';
2+
3+
export interface State {
4+
docX: number;
5+
docY: number;
6+
posX: number;
7+
posY: number;
8+
elX: number;
9+
elY: number;
10+
elH: number;
11+
elW: number;
12+
}
13+
14+
const useMouse = (ref: RefObject<HTMLElement>): State => {
15+
const frame = useRef(0);
16+
const [state, setState] = useState<State>({
17+
docX: 0,
18+
docY: 0,
19+
posX: 0,
20+
posY: 0,
21+
elX: 0,
22+
elY: 0,
23+
elH: 0,
24+
elW: 0,
25+
});
26+
27+
useEffect(() => {
28+
const handler = (event: MouseEvent) => {
29+
cancelAnimationFrame(frame.current)
30+
frame.current = requestAnimationFrame(() => {
31+
if (ref && ref.current) {
32+
const {left, top} = ref.current.getBoundingClientRect()
33+
const posX = left + window.scrollX;
34+
const posY = top + window.scrollY;
35+
36+
setState({
37+
docX: event.pageX,
38+
docY: event.pageY,
39+
posX,
40+
posY,
41+
elX: event.pageX - posX,
42+
elY: event.pageY - posY,
43+
elH: ref.current.offsetHeight,
44+
elW: ref.current.offsetWidth,
45+
});
46+
}
47+
});
48+
}
49+
50+
document.addEventListener('mousemove', handler);
51+
52+
return () => {
53+
cancelAnimationFrame(frame.current);
54+
document.removeEventListener('mousemove', handler);
55+
};
56+
}, []);
57+
58+
return state;
59+
}
60+
61+
export default useMouse

0 commit comments

Comments
 (0)