-
Notifications
You must be signed in to change notification settings - Fork 85
/
index.ts
110 lines (99 loc) · 3.42 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import {useEffect, useState, useRef, useCallback} from 'react';
import {create, AttentionContext, AttentionState} from 'user-attention';
import {useActionPending} from '@huse/action-pending';
import {useStableInterval} from '@huse/timeout';
export interface PollOptions {
minInterval: number;
maxInterval?: number;
maxIdleTime?: number;
stopOnInactive?: boolean;
}
export type PollHook<S> = [S | undefined, number];
const resolveOptions = (options: number | PollOptions) => {
if (typeof options === 'number') {
return {
minInterval: options,
maxInterval: options,
maxIdleTime: 0,
stopOnInactive: false,
};
}
return {
minInterval: options.minInterval,
maxInterval: options.maxInterval || options.minInterval,
maxIdleTime: options.maxIdleTime || 30 * 1000,
stopOnInactive: options.stopOnInactive || false,
};
};
export function usePoll<S>(fetch: () => Promise<S>, options: number | PollOptions): PollHook<S> {
const {minInterval, maxInterval, maxIdleTime, stopOnInactive} = resolveOptions(options);
const attention = useRef<AttentionContext | null>(null);
const lastAttentionState = useRef<AttentionState>('active');
const lastPollTime = useRef(Date.now());
const [poll, pendingCount] = useActionPending(fetch);
const [response, setResponse] = useState<S | undefined>();
const pollAndStore = useCallback(
() => {
lastPollTime.current = Date.now();
poll().then(setResponse);
},
[poll]
);
useEffect(
() => {
const context = create({maxIdleTime});
attention.current = context;
return () => {
context.dispose();
attention.current = null;
};
},
[maxIdleTime]
);
useEffect(
() => pollAndStore(),
[pollAndStore]
);
useStableInterval(
() => {
if (!attention.current) {
pollAndStore();
return;
}
const userAttentionState = attention.current.getState();
if (stopOnInactive && userAttentionState === 'inactive') {
return;
}
const now = Date.now();
const timeEllapsedSinceLastPoll = now - lastPollTime.current;
const minTimeAllowedToPoll = userAttentionState === 'active' ? minInterval : maxInterval - minInterval;
if (timeEllapsedSinceLastPoll >= minTimeAllowedToPoll) {
pollAndStore();
}
},
minInterval
);
useEffect(
() => {
if (!attention.current) {
return;
}
const {subscribe, getState} = attention.current;
const detectActive = () => {
const previousState = lastAttentionState.current;
const currentState = getState();
lastAttentionState.current = currentState;
if (stopOnInactive
&& previousState === 'inactive'
&& currentState === 'active'
&& Date.now() - lastPollTime.current >= minInterval
) {
pollAndStore();
}
};
return subscribe(detectActive);
},
[minInterval, pollAndStore, stopOnInactive]
);
return [response, pendingCount];
}