22 *
33 * Created by rouge on 11/09/2019.
44 * Converted to Typescript on 14/07/2020.
5- *
5+ * Converted to Functional component. on 21/09/2021
66 */
7- import React from 'react' ;
7+ import React , { useCallback , useEffect , useMemo , useRef } from 'react' ;
88import { ScrollView , View , ViewStyle , StyleProp } from 'react-native' ;
99import Dot from './component/Dot' ;
1010import EmptyDot , { defaultEmptyDotSize } from './component/EmptyDot' ;
11+ import { usePrevious } from 'react-use' ;
1112
1213export interface IDotContainerProps {
1314 curPage : number ;
@@ -19,141 +20,50 @@ export interface IDotContainerProps {
1920
2021const ONE_EMPTY_DOT_SIZE = defaultEmptyDotSize * defaultEmptyDotSize ;
2122
22- class DotContainer extends React . Component < IDotContainerProps > {
23- private refScrollView : ScrollView | null = null ;
23+ const DotContainer : React . FC < IDotContainerProps > = ( props ) => {
24+ const refScrollView = useRef < ScrollView > ( null ) ;
25+ const prevPage = usePrevious ( props . curPage ) ;
2426
25- shouldComponentUpdate ( nextProps : IDotContainerProps ) {
26- if ( this . props . curPage === nextProps . curPage ) {
27- // prevent unnecessary re-render caused by external change
28- return false ;
29- }
27+ const getSizeRatio = useCallback < ( ) => number > ( ( ) => {
28+ if ( ! props . sizeRatio ) return 1.0 ;
3029
31- return true ;
32- }
30+ return Math . max ( 1.0 , props . sizeRatio ) ;
31+ } , [ props . sizeRatio ] ) ;
3332
34- componentDidUpdate ( prevProps : IDotContainerProps ) {
35- if ( this . props . maxPage > 4 && prevProps . curPage !== this . props . curPage )
36- this . scrollTo ( this . props . curPage ) ;
37- }
33+ const scrollTo = useCallback < ( index : number , animated ?: boolean ) => void > (
34+ ( index , animated = true ) => {
35+ if ( ! refScrollView ) return ;
3836
39- render ( ) {
40- const { curPage, maxPage, activeDotColor } = this . props ;
41- const list = [ ...Array ( maxPage ) . keys ( ) ] ;
42-
43- let normalizedPage = curPage ;
44- if ( curPage < 0 ) {
45- normalizedPage = 0 ;
46- }
47-
48- if ( curPage > maxPage - 1 ) {
49- normalizedPage = maxPage - 1 ;
50- }
51- const sizeRatio = this . getSizeRatio ( ) ;
52-
53- const container = this . getContainerStyle ( ) ;
54-
55- if ( maxPage < 5 ) {
56- return (
57- < View style = { container } >
58- { list . map ( ( i ) => {
59- return (
60- < Dot
61- key = { i }
62- idx = { i }
63- sizeRatio = { sizeRatio }
64- curPage = { normalizedPage }
65- maxPage = { maxPage }
66- activeColor = { activeDotColor }
67- />
68- ) ;
69- } ) }
70- </ View >
71- ) ;
72- }
37+ const sizeRatio = getSizeRatio ( ) ;
38+ const FIRST_EMPTY_DOT_SPACE = ONE_EMPTY_DOT_SIZE * 2 ;
39+ const MOVE_DISTANCE = ONE_EMPTY_DOT_SIZE * sizeRatio ;
7340
74- return (
75- < View
76- style = { container }
77- onLayout = { ( ) => {
78- // scroll to right index on initial render
79- this . scrollTo ( this . props . curPage , false ) ;
80- } }
81- >
82- < ScrollView
83- ref = { ( ref ) => {
84- this . refScrollView = ref ;
85- } }
86- contentContainerStyle = { {
87- alignItems : 'center' ,
88- } }
89- bounces = { false }
90- horizontal = { ! this . props . vertical }
91- scrollEnabled = { false }
92- showsVerticalScrollIndicator = { false }
93- showsHorizontalScrollIndicator = { false }
94- >
95- { /* previous empty dummy dot */ }
96- < EmptyDot sizeRatio = { sizeRatio } />
97- < EmptyDot sizeRatio = { sizeRatio } />
98-
99- { list . map ( ( i ) => {
100- return (
101- < Dot
102- sizeRatio = { sizeRatio }
103- key = { i }
104- idx = { i }
105- curPage = { normalizedPage }
106- maxPage = { maxPage }
107- activeColor = { activeDotColor }
108- />
109- ) ;
110- } ) }
111-
112- { /* previous empty dummy dot */ }
113- < EmptyDot sizeRatio = { sizeRatio } />
114- < EmptyDot sizeRatio = { sizeRatio } />
115- </ ScrollView >
116- </ View >
117- ) ;
118- }
119-
120- scrollTo ( index : number , animated = true ) {
121- if ( ! this . refScrollView ) return ;
122-
123- const sizeRatio = this . getSizeRatio ( ) ;
124- const FIRST_EMPTY_DOT_SPACE = ONE_EMPTY_DOT_SIZE * 2 ;
125- const MOVE_DISTANCE = ONE_EMPTY_DOT_SIZE * sizeRatio ;
126-
127- const moveTo = Math . max (
128- 0 ,
129- FIRST_EMPTY_DOT_SPACE + ( index - 4 ) * MOVE_DISTANCE
130- ) ;
41+ const moveTo = Math . max (
42+ 0 ,
43+ FIRST_EMPTY_DOT_SPACE + ( index - 4 ) * MOVE_DISTANCE
44+ ) ;
13145
132- if ( this . props . vertical ) {
133- this . refScrollView . scrollTo ( {
134- x : 0 ,
135- y : moveTo ,
46+ if ( props . vertical ) {
47+ refScrollView . current ?. scrollTo ( {
48+ x : 0 ,
49+ y : moveTo ,
50+ animated,
51+ } ) ;
52+ return ;
53+ }
54+
55+ refScrollView . current ?. scrollTo ( {
56+ x : moveTo ,
57+ y : 0 ,
13658 animated,
13759 } ) ;
138- return ;
139- }
140-
141- this . refScrollView . scrollTo ( {
142- x : moveTo ,
143- y : 0 ,
144- animated,
145- } ) ;
146- }
147-
148- getSizeRatio ( ) : number {
149- if ( ! this . props . sizeRatio ) return 1.0 ;
60+ } ,
61+ [ getSizeRatio , props . vertical ]
62+ ) ;
15063
151- return Math . max ( 1.0 , this . props . sizeRatio ) ;
152- }
153-
154- getContainerStyle ( ) : StyleProp < ViewStyle > {
155- const { vertical } = this . props ;
156- const sizeRatio = this . getSizeRatio ( ) ;
64+ const getContainerStyle = useCallback < ( ) => StyleProp < ViewStyle > > ( ( ) => {
65+ const { vertical } = props ;
66+ const sizeRatio = getSizeRatio ( ) ;
15767 const containerSize = 84 * sizeRatio ;
15868
15969 return {
@@ -162,7 +72,89 @@ class DotContainer extends React.Component<IDotContainerProps> {
16272 maxHeight : vertical ? containerSize : undefined ,
16373 maxWidth : vertical ? undefined : containerSize ,
16474 } ;
75+ } , [ getSizeRatio , props ] ) ;
76+
77+ useEffect ( ( ) => {
78+ if ( props . maxPage > 4 && prevPage !== props . curPage )
79+ scrollTo ( props . curPage ) ;
80+ } , [ prevPage , props . curPage , props . maxPage , scrollTo ] ) ;
81+
82+ const { curPage, maxPage, activeDotColor } = props ;
83+ const list = useMemo ( ( ) => [ ...Array ( maxPage ) . keys ( ) ] , [ maxPage ] ) ;
84+
85+ let normalizedPage = curPage ;
86+ if ( curPage < 0 ) {
87+ normalizedPage = 0 ;
16588 }
166- }
89+
90+ if ( curPage > maxPage - 1 ) {
91+ normalizedPage = maxPage - 1 ;
92+ }
93+ const sizeRatio = getSizeRatio ( ) ;
94+
95+ const container = getContainerStyle ( ) ;
96+
97+ if ( maxPage < 5 ) {
98+ return (
99+ < View style = { container } >
100+ { list . map ( ( i ) => {
101+ return (
102+ < Dot
103+ key = { i }
104+ idx = { i }
105+ sizeRatio = { sizeRatio }
106+ curPage = { normalizedPage }
107+ maxPage = { maxPage }
108+ activeColor = { activeDotColor }
109+ />
110+ ) ;
111+ } ) }
112+ </ View >
113+ ) ;
114+ }
115+
116+ return (
117+ < View
118+ style = { container }
119+ onLayout = { ( ) => {
120+ // scroll to right index on initial render
121+ scrollTo ( props . curPage , false ) ;
122+ } }
123+ >
124+ < ScrollView
125+ ref = { refScrollView }
126+ contentContainerStyle = { {
127+ alignItems : 'center' ,
128+ } }
129+ bounces = { false }
130+ horizontal = { ! props . vertical }
131+ scrollEnabled = { false }
132+ showsVerticalScrollIndicator = { false }
133+ showsHorizontalScrollIndicator = { false }
134+ >
135+ { /* previous empty dummy dot */ }
136+ < EmptyDot sizeRatio = { sizeRatio } />
137+ < EmptyDot sizeRatio = { sizeRatio } />
138+
139+ { list . map ( ( i ) => {
140+ return (
141+ < Dot
142+ sizeRatio = { sizeRatio }
143+ key = { i }
144+ idx = { i }
145+ curPage = { normalizedPage }
146+ maxPage = { maxPage }
147+ activeColor = { activeDotColor }
148+ />
149+ ) ;
150+ } ) }
151+
152+ { /* previous empty dummy dot */ }
153+ < EmptyDot sizeRatio = { sizeRatio } />
154+ < EmptyDot sizeRatio = { sizeRatio } />
155+ </ ScrollView >
156+ </ View >
157+ ) ;
158+ } ;
167159
168160export default DotContainer ;
0 commit comments