1
1
import * as React from 'react' ;
2
- const memoizeOne = require ( 'memoize-one' ) ;
3
- const memoize =
4
- typeof memoizeOne === 'function' ? memoizeOne : memoizeOne . default ;
5
2
const raf = require ( 'raf' ) ;
6
3
7
- import { shallowEqualScroll , shallowEqualDimensions } from './utils' ;
8
- import { IScroll , IDimensions } from './types' ;
9
-
10
4
import {
11
5
Consumer ,
12
- createInitScrollState ,
13
6
createInitDimensionsState ,
14
- IScroll as IContextScroll ,
15
- SCROLL_DIR_UP ,
16
- SCROLL_DIR_DOWN ,
17
- SCROLL_DIR_RIGHT ,
18
- SCROLL_DIR_LEFT ,
7
+ createInitScrollState ,
19
8
} from './ViewportProvider' ;
20
-
21
- interface IState extends IContextScroll , IDimensions { }
9
+ import {
10
+ IScroll ,
11
+ IDimensions ,
12
+ IViewport ,
13
+ TViewportChangeHandler ,
14
+ } from './types' ;
22
15
23
16
export interface IChildProps {
24
17
scroll : IScroll | null ;
25
18
dimensions : IDimensions | null ;
26
19
}
27
20
21
+ interface IState extends IChildProps { }
22
+
28
23
interface IProps {
29
24
children ?: ( props : IChildProps ) => React . ReactNode ;
30
25
onUpdate ?: ( props : IChildProps ) => void ;
31
26
disableScrollUpdates : boolean ;
32
27
disableDimensionsUpdates : boolean ;
33
28
}
34
29
30
+ interface IContext {
31
+ addViewportChangeListener : ( fn : TViewportChangeHandler ) => void ;
32
+ removeViewportChangeListener : ( fn : TViewportChangeHandler ) => void ;
33
+ }
34
+
35
35
export default class ObserveViewport extends React . Component < IProps , IState > {
36
- tickId : NodeJS . Timer ;
37
- scrollContext : IContextScroll ;
38
- dimensionsContext : IDimensions ;
36
+ private addViewportChangeListener :
37
+ | ( ( fn : TViewportChangeHandler ) => void )
38
+ | null ;
39
+ private removeViewportChangeListener :
40
+ | ( ( fn : TViewportChangeHandler ) => void )
41
+ | null ;
42
+
43
+ private tickId : NodeJS . Timer ;
39
44
40
45
static defaultProps = {
41
46
disableScrollUpdates : false ,
@@ -44,100 +49,70 @@ export default class ObserveViewport extends React.Component<IProps, IState> {
44
49
45
50
constructor ( props : IProps ) {
46
51
super ( props ) ;
47
- this . scrollContext = createInitScrollState ( ) ;
48
- this . dimensionsContext = createInitDimensionsState ( ) ;
49
52
this . state = {
50
- ... this . scrollContext ,
51
- ... this . dimensionsContext ,
53
+ scroll : createInitScrollState ( ) ,
54
+ dimensions : createInitDimensionsState ( ) ,
52
55
} ;
53
56
}
54
57
55
58
shouldComponentUpdate ( nextProps : IProps ) {
56
59
return Boolean ( nextProps . children ) ;
57
60
}
58
61
59
- componentDidMount ( ) {
60
- this . tick ( this . syncState ) ;
61
- }
62
-
63
62
componentWillUnmount ( ) {
63
+ if ( this . removeViewportChangeListener ) {
64
+ this . removeViewportChangeListener ( this . handleViewportUpdate ) ;
65
+ }
66
+ this . removeViewportChangeListener = null ;
67
+ this . addViewportChangeListener = null ;
64
68
raf . cancel ( this . tickId ) ;
65
69
}
66
70
67
- getPublicScroll = memoize (
68
- ( scroll : IScroll ) : IScroll => scroll ,
69
- shallowEqualScroll ,
70
- ) ;
71
-
72
- getPublicDimensions = memoize (
73
- ( dimensions : IDimensions ) : IDimensions => dimensions ,
74
- shallowEqualDimensions ,
75
- ) ;
76
-
77
- storeContext = ( scrollContext : {
78
- scroll : IContextScroll ;
79
- dimensions : IDimensions ;
80
- } ) => {
81
- this . scrollContext = scrollContext . scroll ;
82
- this . dimensionsContext = scrollContext . dimensions ;
83
- return null ;
84
- } ;
71
+ handleViewportUpdate = ( viewport : IViewport ) => {
72
+ const scroll = this . props . disableScrollUpdates ? null : viewport . scroll ;
73
+ const dimensions = this . props . disableDimensionsUpdates
74
+ ? null
75
+ : viewport . dimensions ;
76
+ const nextViewport = {
77
+ scroll : scroll ,
78
+ dimensions : dimensions ,
79
+ } ;
80
+
81
+ if ( this . props . onUpdate ) {
82
+ this . props . onUpdate ( nextViewport ) ;
83
+ }
85
84
86
- tick ( updater : ( ) => void ) {
87
85
this . tickId = raf ( ( ) => {
88
- if ( this ) {
89
- updater ( ) ;
90
- this . tick ( updater ) ;
91
- }
86
+ this . setState ( nextViewport ) ;
92
87
} ) ;
93
- }
88
+ } ;
94
89
95
- syncState = ( ) => {
96
- const { disableScrollUpdates, disableDimensionsUpdates } = this . props ;
97
- const nextState = {
98
- ...this . scrollContext ,
99
- ...this . dimensionsContext ,
100
- } ;
101
- const scrollDidUpdate = disableScrollUpdates
102
- ? false
103
- : ! shallowEqualScroll ( nextState as any , this . state as any ) ;
104
- const dimensionsDidUpdate = disableDimensionsUpdates
105
- ? false
106
- : ! shallowEqualDimensions ( nextState as any , this . state as any ) ;
107
-
108
- if ( scrollDidUpdate || dimensionsDidUpdate ) {
109
- if ( this . props . onUpdate ) {
110
- this . props . onUpdate ( this . getPropsFromState ( nextState ) ) ;
111
- }
112
- this . setState ( nextState ) ;
90
+ registerViewportListeners = ( {
91
+ addViewportChangeListener,
92
+ removeViewportChangeListener,
93
+ } : IContext ) : null => {
94
+ const shouldRegister =
95
+ this . removeViewportChangeListener !== removeViewportChangeListener &&
96
+ this . addViewportChangeListener !== addViewportChangeListener ;
97
+
98
+ if ( ! shouldRegister ) {
99
+ return null ;
113
100
}
114
- } ;
115
101
116
- getPropsFromState ( state : IState = this . state ) {
117
- const { disableScrollUpdates, disableDimensionsUpdates } = this . props ;
118
- const { xDir, yDir, width, height, ...scroll } = state ;
119
- return {
120
- scroll : disableScrollUpdates
121
- ? null
122
- : this . getPublicScroll ( {
123
- ...scroll ,
124
- isScrollingUp : yDir === SCROLL_DIR_UP ,
125
- isScrollingDown : yDir === SCROLL_DIR_DOWN ,
126
- isScrollingLeft : xDir === SCROLL_DIR_LEFT ,
127
- isScrollingRight : xDir === SCROLL_DIR_RIGHT ,
128
- } ) ,
129
- dimensions : disableDimensionsUpdates
130
- ? null
131
- : this . getPublicDimensions ( { width, height } ) ,
132
- } ;
133
- }
102
+ if ( this . removeViewportChangeListener ) {
103
+ this . removeViewportChangeListener ( this . handleViewportUpdate ) ;
104
+ }
105
+ this . removeViewportChangeListener = removeViewportChangeListener ;
106
+ addViewportChangeListener ( this . handleViewportUpdate ) ;
107
+ return null ;
108
+ } ;
134
109
135
110
render ( ) {
136
111
const { children } = this . props ;
137
112
return (
138
113
< React . Fragment >
139
- < Consumer > { this . storeContext } </ Consumer >
140
- { typeof children === 'function' && children ( this . getPropsFromState ( ) ) }
114
+ < Consumer > { this . registerViewportListeners } </ Consumer >
115
+ { typeof children === 'function' && children ( this . state ) }
141
116
</ React . Fragment >
142
117
) ;
143
118
}
0 commit comments