forked from stutrek/scrollmonitor-hooks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
101 lines (83 loc) · 2.33 KB
/
index.js
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
import React, { useLayoutEffect, useState, useRef, useContext, createContext } from 'react';
import scrollmonitor from 'scrollmonitor';
const attrs = [
'isInViewport',
'isFullyInViewport',
'isAboveViewport',
'isBelowViewport',
'top',
'bottom',
'height',
];
const defaultState = {
isInViewport: 0,
isFullyInViewport: 0,
isAboveViewport: 0,
isBelowViewport: 0,
top: 0,
bottom: 0,
height: 0,
};
const ScrollContainerContext = createContext(scrollmonitor);
export const useScrollMonitor = (ref, callbacks, offsets=0) => {
let { current } = ref;
const [waitCount, updateWaitCount] = useState(0);
const scrollMonitorContainer = useContext(ScrollContainerContext);
const updateCheck = typeof offsets === 'number'
? [current, callbacks, scrollMonitorContainer, offsets]
: [current, callbacks, scrollMonitorContainer, offsets.top, offsets.bottom];
useLayoutEffect(() => {
if (current === null) {
updateWaitCount(waitCount+1);
return;
}
const watcher = scrollMonitorContainer.create(current, offsets);
for (let eventName in callbacks) {
watcher.on(eventName, () => callbacks[eventName](watcher));
watcher.update();
watcher.triggerCallbacks();
if (callbacks.stateChange) {
callbacks.stateChange(watcher);
}
}
return () => {
watcher.destroy();
};
}, updateCheck);
return;
};
const createUpdatedState = (watcher) => {
return attrs.reduce((acc, attr) => {
acc[attr] = watcher[attr];
return acc;
}, {});
};
export const useScrollState = (ref, offsets=0) => {
const [state, updateState] = useState(defaultState);
const [callbacks] = useState({
stateChange: (watcher) => {
updateState(createUpdatedState(watcher));
}
});
useScrollMonitor(ref, callbacks, offsets);
return state;
};
const useScrollContainer = (ref) => {
const [container, setContainer] = useState(scrollmonitor);
const [waitCount, updateWaitCount] = useState(0);
useLayoutEffect(() => {
if (ref.current === null) {
updateWaitCount(waitCount+1);
return;
}
setContainer(scrollmonitor.createContainer(ref.current));
}, [ref.current]);
return container;
};
export const withScrollContainer = Component => props => {
const ref = useRef(null);
const container = useScrollContainer(ref);
return <ScrollContainerContext.Provider value={container}>
<Component ref={ref} {...props} />
</ScrollContainerContext.Provider>;
};