Skip to content

Commit 74d3ece

Browse files
committed
feat: 🎸 add useIdle hook
1 parent 5881fa6 commit 74d3ece

File tree

7 files changed

+109
-1
lines changed

7 files changed

+109
-1
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# react-use
22

3+
![](https://img.shields.io/badge/React-%F0%9F%8E%A3%20hooks-orange.svg?longCache=true&style=flat)
4+
35
Collection of essential React Hooks.
46
This is port of [`libreact`](https://github.com/streamich/libreact) to React Hooks.
57

@@ -15,6 +17,7 @@ This is port of [`libreact`](https://github.com/streamich/libreact) to React Hoo
1517
- [`useBattery`](./docs/useBattery.md)
1618
- [`useGeolocation`](./docs/useGeolocation.md)
1719
- [`useHover`](./docs/useHover.md)
20+
- [`useIdle`](./docs/useIdle.md)
1821
- [`useLocation`](./docs/useLocation.md)
1922
- [`useMedia`](./docs/useMedia.md)
2023
- [`useMediaDevices`](./docs/useMediaDevices.md)

docs/useIdle.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `useIdle`
2+
3+
React sensor hook that tracks if user on the page is idle.
4+
5+
6+
## Usage
7+
8+
```jsx
9+
import {useIdle} from 'react-use';
10+
11+
const Demo = () => {
12+
const isIdle = useIdle(3e3);
13+
14+
return (
15+
<div>
16+
<div>Use is idle: {isIdle ? 'Yes' : 'No'}</div>
17+
</div>
18+
);
19+
};
20+
```
21+
22+
23+
## Reference
24+
25+
```js
26+
useIdle(ms, initialState);
27+
```
28+
29+
- `ms` &mdash; time in milliseconds after which to consider use idle, defaults to `60e3` &mdash; one minute.
30+
- `initialState` &mdash; whether to consider user initially idle, defaults to false.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"license": "Unlicense",
1515
"dependencies": {
1616
"@types/react": "^16.4.18",
17-
"@types/react-dom": "^16.0.9"
17+
"@types/react-dom": "^16.0.9",
18+
"throttle-debounce": "^2.0.1"
1819
},
1920
"devDependencies": {
2021
"@storybook/react": "^3.4.11",

src/__stories__/useIdle.story.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {storiesOf} from '@storybook/react';
2+
import * as React from 'react';
3+
import {useIdle} from '..';
4+
5+
const Demo = () => {
6+
const isIdle = useIdle(3e3);
7+
8+
return (
9+
<div>
10+
<div>Use is idle: {isIdle ? 'Yes' : 'No'}</div>
11+
</div>
12+
);
13+
};
14+
15+
storiesOf('useIdle', module)
16+
.add('Example', () =>
17+
<Demo/>
18+
)

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import useBattery from './useBattery';
22
import useCounter from './useCounter';
33
import useGeolocation from './useGeolocation';
44
import useHover from './useHover';
5+
import useIdle from './useIdle';
56
import useList from './useList';
67
import useLocation from './useLocation';
78
import useMap from './useMap';
@@ -19,6 +20,7 @@ export {
1920
useCounter,
2021
useGeolocation,
2122
useHover,
23+
useIdle,
2224
useList,
2325
useLocation,
2426
useMap,

src/useIdle.ts

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {useState, useEffect} from './react';
2+
import {on, off} from './util';
3+
import {throttle} from 'throttle-debounce';
4+
5+
const defaultEvents = ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'];
6+
const oneMinute = 60e3;
7+
8+
const useIdle = (ms: number = oneMinute, initialState: boolean = false, events: string[] = defaultEvents): boolean => {
9+
const [state, setState] = useState<boolean>(initialState);
10+
11+
useEffect(() => {
12+
let timeout: any;
13+
let localState: boolean = state;
14+
const set = (newState: boolean) => {
15+
localState = newState;
16+
setState(newState);
17+
};
18+
19+
const onEvent = throttle(50, () => {
20+
if (localState) {
21+
set(false);
22+
}
23+
24+
clearTimeout(timeout);
25+
timeout = setTimeout(() => set(true), ms);
26+
});
27+
const onVisibility = () => {
28+
if (!document.hidden) onEvent();
29+
};
30+
31+
for (let i = 0; i < events.length; i++) {
32+
on(window, events[i], onEvent);
33+
}
34+
on(document, 'visibilitychange', onVisibility);
35+
36+
timeout = setTimeout(() => set(true), ms);
37+
38+
return () => {
39+
for (let i = 0; i < events.length; i++) {
40+
off(window, events[i], onEvent);
41+
}
42+
off(document, 'visibilitychange', onVisibility);
43+
};
44+
}, events);
45+
46+
return state;
47+
};
48+
49+
export default useIdle;

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -6487,6 +6487,11 @@ [email protected]:
64876487
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
64886488
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
64896489

6490+
throttle-debounce@^2.0.1:
6491+
version "2.0.1"
6492+
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.0.1.tgz#7307ddd6cd9acadb349132fbf6c18d78c88a5e62"
6493+
integrity sha512-Sr6jZBlWShsAaSXKyNXyNicOrJW/KtkDqIEwHt4wYwWA2wa/q67Luhqoujg48V8hTk60wB56tYrJJn6jc2R7VA==
6494+
64906495
through2@^2.0.0:
64916496
version "2.0.3"
64926497
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"

0 commit comments

Comments
 (0)