1
- import { useState , useRef , useLayoutEffect , useCallback , useMemo } from 'react' ;
1
+ import { useState , useMemo , useCallback , useRef , useLayoutEffect , } from 'react' ;
2
2
3
- function getVpWidth ( ) {
3
+ function getVpWidth ( ) {
4
4
return ( typeof window != 'undefined' ) ? Math . max (
5
5
window . document . documentElement . clientWidth ,
6
6
window . innerWidth || 0
7
- ) : 0 ;
7
+ ) : 0 ;
8
8
}
9
9
10
10
11
- function getVpHeight ( ) {
11
+ function getVpHeight ( ) {
12
12
return ( typeof window != 'undefined' ) ? Math . max (
13
13
window . document . documentElement . clientHeight ,
14
14
window . innerHeight || 0
@@ -19,15 +19,12 @@ function getVpHeight () {
19
19
// Shared State //
20
20
// =============== //
21
21
22
- // NOTE: using vars and separating since Babel
23
- // transpilation saves a bit of filesize here
24
-
25
22
/**
26
23
* listening component functions
27
24
*
28
25
* @type Function
29
26
*/
30
- var listeners = new Set ( ) ;
27
+ const listeners = new Set ( ) ;
31
28
32
29
/**
33
30
* contains a "hasher" which manages the behavior
@@ -36,12 +33,16 @@ var listeners = new Set();
36
33
* for minimum overhead and so we can reference
37
34
* it easily on deletion
38
35
*
39
- * @type Map<Object, {{ hasher: Function, prevHash: Any }}>
36
+ * @type Map<Object, {{
37
+ * hasher: Function,
38
+ * prevHash: Any,
39
+ * options
40
+ * }}>
40
41
*/
41
- var resolverMap = new Map ( ) ;
42
+ const resolverMap = new Map ( ) ;
42
43
43
- var vpWidth = getVpWidth ( ) ;
44
- var vpHeight = getVpHeight ( ) ;
44
+ let vpWidth = getVpWidth ( ) ;
45
+ let vpHeight = getVpHeight ( ) ;
45
46
46
47
// should only be called by *one* component once;
47
48
// will iterate through all subscribers
@@ -51,20 +52,26 @@ function onResize() {
51
52
vpWidth = getVpWidth ( ) ;
52
53
vpHeight = getVpHeight ( ) ;
53
54
54
- listeners . forEach ( function ( listener ) {
55
+ listeners . forEach ( listener => {
55
56
const params = { vpW : vpWidth , vpH : vpHeight } ;
56
57
57
- if ( ! resolverMap . has ( listener ) ) {
58
- listener ( params ) ;
58
+ let shouldRun = false ;
59
+ let hash ;
60
+
61
+ const { options, prevHash= undefined } = resolverMap ?. get ( listener ) || { } ;
62
+ const { hasher } = options ;
63
+
64
+ if ( ! options ?. hasher ) {
65
+ shouldRun = true ;
59
66
}
60
67
else {
61
- const { hasher, prevHash } = resolverMap . get ( listener ) ;
62
- const hash = hasher ( params ) ;
68
+ hash = hasher ( params ) ;
69
+ if ( hash != prevHash ) { shouldRun = true }
70
+ }
63
71
64
- if ( hash != prevHash ) {
65
- listener ( { ...params , hash } ) ;
66
- resolverMap . set ( listener , { hasher, prevHash : hash } ) ;
67
- }
72
+ if ( shouldRun ) {
73
+ resolverMap . set ( listener , { options, prevHash : hash } ) ;
74
+ listener ( { ...params , options, hash } ) ;
68
75
}
69
76
} ) ;
70
77
}
@@ -85,24 +92,50 @@ function onResize() {
85
92
* @input {Function|Number} input
86
93
*/
87
94
function useViewportSizes ( input ) {
88
- const hasCustomHasher = ( typeof input == 'function' ) ;
89
- const [ state , setState ] = useState ( ( ) => ! hasCustomHasher ?
90
- ( { vpW : vpWidth , vpH : vpHeight } ) :
91
- ( input && input ( { vpW : vpWidth , vpH :vpHeight } ) )
92
- ) ;
93
- const timeout = useRef ( undefined ) ;
94
- const listener = useCallback (
95
- ( ! input || hasCustomHasher ) ?
96
- state => setState ( state ) :
97
- state => {
98
- if ( timeout . current ) { clearTimeout ( timeout . current ) }
99
- timeout . current = setTimeout ( ( ) => setState ( state ) , input ) ;
100
- } , [ input ] ) ;
95
+ const hasher = ( ( typeof input == 'function' ) ?
96
+ input :
97
+ input ?. hasher
98
+ ) || undefined ;
99
+
100
+ const debounceTimeout = input ?. debounceTimeout || undefined ;
101
+
102
+ const throttleTimeout = ( ( typeof input == 'number' ) ?
103
+ input :
104
+ input ?. throttleTimeout
105
+ ) || undefined ;
106
+
107
+ const dimension = input ?. dimension || 'both' ;
108
+
109
+ const options = {
110
+ ...( typeof input == 'function' ? { } : input ) ,
111
+ dimension,
112
+ hasher
113
+ } ;
114
+
115
+ const [ state , setState ] = useState ( ( ) => ( ! hasher ?
116
+ { vpW : vpWidth , vpH : vpHeight } :
117
+ hasher && hasher ( { vpW : vpWidth , vpH : vpHeight } )
118
+ ) ) ;
119
+ const debounceTimeoutRef = useRef ( undefined ) ;
120
+ const listener = useCallback ( ( ! hasher ?
121
+ state => setState ( state ) :
122
+ state => {
123
+ if ( debounceTimeoutRef . current ) {
124
+ clearTimeout ( debounceTimeoutRef . current ) ;
125
+ }
101
126
102
- useLayoutEffect ( ( ) => {
103
- if ( hasCustomHasher ) {
104
- resolverMap . set ( listener , { hasher : input , prevHash : state . hash } ) ;
127
+ debounceTimeoutRef . current = setTimeout ( ( ) => {
128
+ setState ( state ) ;
129
+ } , debounceTimeoutRef ) ;
105
130
}
131
+ ) , [ debounceTimeoutRef , hasher , dimension ] ) ;
132
+
133
+ useLayoutEffect ( ( ) => {
134
+ resolverMap . set ( listener , {
135
+ options,
136
+ prevHash : state . hash || undefined
137
+ } ) ;
138
+
106
139
listeners . add ( listener ) ;
107
140
108
141
if ( window && listeners . size == 1 ) {
@@ -117,16 +150,39 @@ function useViewportSizes(input) {
117
150
listeners . delete ( listener ) ;
118
151
119
152
if ( listeners . size == 0 ) {
120
- window . removeEventListener ( 'resize' , onResize ) ;
153
+ window ? .removeEventListener ?. ( 'resize' , onResize ) ;
121
154
}
122
155
} ;
123
156
} , [ listener ] ) ;
124
157
125
- const returnValue = useMemo ( ( ) => ( [
126
- state . vpW , state . vpH , hasCustomHasher ? state . hash : onResize
127
- ] ) , [ state , onResize ] )
158
+ let dimensionHash ;
159
+
160
+ switch ( dimension ) {
161
+ default :
162
+ case 'both' : {
163
+ dimensionHash = `${ state ?. vpW } _${ state . vpH } ` ;
164
+ break ;
165
+ }
166
+ case 'w' : {
167
+ dimensionHash = state ?. vpW || 0 ;
168
+ break ;
169
+ }
170
+ case 'h' : {
171
+ dimensionHash = state ?. vpH || 0 ;
172
+ break ;
173
+ }
174
+ }
175
+
176
+ const returnValue = useMemo ( ( ) => {
177
+ switch ( dimension ) {
178
+ default :
179
+ case 'both' : { return [ state ?. vpW || 0 , state ?. vpH || 0 ] }
180
+ case 'w' : { return state ?. vpW || 0 }
181
+ case 'h' : { return state ?. vpH || 0 }
182
+ }
183
+ } , [ dimensionHash , onResize , dimension ] ) ;
128
184
129
185
return returnValue ;
130
186
}
131
187
132
- export default useViewportSizes
188
+ export default useViewportSizes ;
0 commit comments