@@ -102,20 +102,28 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
102
102
/**
103
103
* Always point to the latest props if `disabled` is `false`
104
104
*/
105
- cachedProps : Partial < ListProps < T > > = { } ;
105
+ cachedProps : Partial < ListProps < T > > ;
106
106
107
107
/**
108
108
* Lock scroll process with `onScroll` event.
109
109
* This is used for `data` length change and `scrollTop` restore
110
110
*/
111
111
lockScroll : boolean = false ;
112
112
113
+ constructor ( props : ListProps < T > ) {
114
+ super ( props ) ;
115
+
116
+ this . cachedProps = props ;
117
+ }
118
+
113
119
/**
114
120
* Phase 1: Initial should sync with default scroll top
115
121
*/
116
122
public componentDidMount ( ) {
117
- this . listRef . current . scrollTop = 0 ;
118
- this . onScroll ( ) ;
123
+ if ( this . listRef . current ) {
124
+ this . listRef . current . scrollTop = 0 ;
125
+ this . onScroll ( ) ;
126
+ }
119
127
}
120
128
121
129
/**
@@ -127,7 +135,7 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
127
135
const { data, height, itemHeight, disabled } = this . props ;
128
136
const prevData : T [ ] = this . cachedProps . data || [ ] ;
129
137
130
- if ( disabled ) {
138
+ if ( disabled || ! this . listRef . current ) {
131
139
return ;
132
140
}
133
141
@@ -209,7 +217,7 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
209
217
itemElementHeights : this . itemElementHeights ,
210
218
} ) ;
211
219
212
- this . scrollTo ( {
220
+ this . internalScrollTo ( {
213
221
itemIndex : originCompareItemIndex ,
214
222
relativeTop : originCompareItemTop ,
215
223
} ) ;
@@ -262,6 +270,7 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
262
270
263
271
const item = data [ index ] ;
264
272
if ( ! item ) {
273
+ /* istanbul ignore next */
265
274
console . error ( 'Not find index item. Please report this since it is a bug.' ) ;
266
275
}
267
276
@@ -286,106 +295,106 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
286
295
}
287
296
} ;
288
297
289
- public scrollTo ( arg : number | RelativeScroll ) : void {
290
- if ( typeof arg === 'number' ) {
291
- this . listRef . current . scrollTop = arg ;
292
- } else if ( typeof arg === 'object' ) {
293
- const { itemIndex : compareItemIndex , relativeTop : compareItemRelativeTop } = arg ;
294
- const { scrollTop : originScrollTop } = this . state ;
295
- const { data, itemHeight, height } = this . props ;
298
+ public scrollTo ( scrollTop : number ) {
299
+ this . listRef . current . scrollTop = scrollTop ;
300
+ }
301
+
302
+ public internalScrollTo ( relativeScroll : RelativeScroll ) : void {
303
+ const { itemIndex : compareItemIndex , relativeTop : compareItemRelativeTop } = relativeScroll ;
304
+ const { scrollTop : originScrollTop } = this . state ;
305
+ const { data, itemHeight, height } = this . props ;
296
306
297
- // 1. Find the best match compare item top
298
- let bestSimilarity = Number . MAX_VALUE ;
299
- let bestScrollTop : number = null ;
300
- let bestItemIndex : number = null ;
301
- let bestItemOffsetPtg : number = null ;
302
- let bestStartIndex : number = null ;
303
- let bestEndIndex : number = null ;
307
+ // 1. Find the best match compare item top
308
+ let bestSimilarity = Number . MAX_VALUE ;
309
+ let bestScrollTop : number = null ;
310
+ let bestItemIndex : number = null ;
311
+ let bestItemOffsetPtg : number = null ;
312
+ let bestStartIndex : number = null ;
313
+ let bestEndIndex : number = null ;
304
314
305
- let missSimilarity = 0 ;
315
+ let missSimilarity = 0 ;
306
316
307
- const scrollHeight = data . length * itemHeight ;
308
- const { clientHeight } = this . listRef . current ;
309
- const maxScrollTop = scrollHeight - clientHeight ;
317
+ const scrollHeight = data . length * itemHeight ;
318
+ const { clientHeight } = this . listRef . current ;
319
+ const maxScrollTop = scrollHeight - clientHeight ;
310
320
311
- for ( let i = 0 ; i < maxScrollTop ; i += 1 ) {
312
- const scrollTop = getIndexByStartLoc ( 0 , maxScrollTop , originScrollTop , i ) ;
321
+ for ( let i = 0 ; i < maxScrollTop ; i += 1 ) {
322
+ const scrollTop = getIndexByStartLoc ( 0 , maxScrollTop , originScrollTop , i ) ;
313
323
314
- const scrollPtg = getScrollPercentage ( { scrollTop, scrollHeight, clientHeight } ) ;
315
- const visibleCount = Math . ceil ( height / itemHeight ) ;
324
+ const scrollPtg = getScrollPercentage ( { scrollTop, scrollHeight, clientHeight } ) ;
325
+ const visibleCount = Math . ceil ( height / itemHeight ) ;
316
326
317
- const { itemIndex, itemOffsetPtg, startIndex, endIndex } = getRangeIndex (
327
+ const { itemIndex, itemOffsetPtg, startIndex, endIndex } = getRangeIndex (
328
+ scrollPtg ,
329
+ data . length ,
330
+ visibleCount ,
331
+ ) ;
332
+
333
+ // No need to check if compare item out of the index to save performance
334
+ if ( startIndex <= compareItemIndex && compareItemIndex <= endIndex ) {
335
+ // 1.1 Get measure located item relative top
336
+ const locatedItemRelativeTop = getItemRelativeTop ( {
337
+ itemIndex,
338
+ itemOffsetPtg,
339
+ itemElementHeights : this . itemElementHeights ,
318
340
scrollPtg,
319
- data . length ,
320
- visibleCount ,
321
- ) ;
322
-
323
- // No need to check if compare item out of the index to save performance
324
- if ( startIndex <= compareItemIndex && compareItemIndex <= endIndex ) {
325
- // 1.1 Get measure located item relative top
326
- const locatedItemRelativeTop = getItemRelativeTop ( {
327
- itemIndex,
328
- itemOffsetPtg,
329
- itemElementHeights : this . itemElementHeights ,
330
- scrollPtg,
331
- clientHeight,
332
- getItemKey : this . getIndexKey ,
333
- } ) ;
334
-
335
- const compareItemTop = getCompareItemRelativeTop ( {
336
- locatedItemRelativeTop,
337
- locatedItemIndex : itemIndex ,
338
- compareItemIndex, // Same as origin index
339
- startIndex,
340
- endIndex,
341
- getItemKey : this . getIndexKey ,
342
- itemElementHeights : this . itemElementHeights ,
343
- } ) ;
344
-
345
- // 1.2 Find best match compare item top
346
- const similarity = Math . abs ( compareItemTop - compareItemRelativeTop ) ;
347
- if ( similarity < bestSimilarity ) {
348
- bestSimilarity = similarity ;
349
- bestScrollTop = scrollTop ;
350
- bestItemIndex = itemIndex ;
351
- bestItemOffsetPtg = itemOffsetPtg ;
352
- bestStartIndex = startIndex ;
353
- bestEndIndex = endIndex ;
354
-
355
- missSimilarity = 0 ;
356
- } else {
357
- missSimilarity += 1 ;
358
- }
359
- }
341
+ clientHeight,
342
+ getItemKey : this . getIndexKey ,
343
+ } ) ;
360
344
361
- // If keeping 10 times not match similarity,
362
- // check more scrollTop is meaningless.
363
- // Here boundary is set to 10.
364
- if ( missSimilarity > 10 ) {
365
- break ;
345
+ const compareItemTop = getCompareItemRelativeTop ( {
346
+ locatedItemRelativeTop,
347
+ locatedItemIndex : itemIndex ,
348
+ compareItemIndex, // Same as origin index
349
+ startIndex,
350
+ endIndex,
351
+ getItemKey : this . getIndexKey ,
352
+ itemElementHeights : this . itemElementHeights ,
353
+ } ) ;
354
+
355
+ // 1.2 Find best match compare item top
356
+ const similarity = Math . abs ( compareItemTop - compareItemRelativeTop ) ;
357
+ if ( similarity < bestSimilarity ) {
358
+ bestSimilarity = similarity ;
359
+ bestScrollTop = scrollTop ;
360
+ bestItemIndex = itemIndex ;
361
+ bestItemOffsetPtg = itemOffsetPtg ;
362
+ bestStartIndex = startIndex ;
363
+ bestEndIndex = endIndex ;
364
+
365
+ missSimilarity = 0 ;
366
+ } else {
367
+ missSimilarity += 1 ;
366
368
}
367
369
}
368
370
369
- // 2. Re-scroll if has best scroll match
370
- if ( bestScrollTop !== null ) {
371
- this . lockScroll = true ;
372
- this . listRef . current . scrollTop = bestScrollTop ;
373
-
374
- this . setState ( {
375
- status : 'MEASURE_START' ,
376
- scrollTop : bestScrollTop ,
377
- itemIndex : bestItemIndex ,
378
- itemOffsetPtg : bestItemOffsetPtg ,
379
- startIndex : bestStartIndex ,
380
- endIndex : bestEndIndex ,
381
- } ) ;
371
+ // If keeping 10 times not match similarity,
372
+ // check more scrollTop is meaningless.
373
+ // Here boundary is set to 10.
374
+ if ( missSimilarity > 10 ) {
375
+ break ;
376
+ }
377
+ }
378
+
379
+ // 2. Re-scroll if has best scroll match
380
+ if ( bestScrollTop !== null ) {
381
+ this . lockScroll = true ;
382
+ this . listRef . current . scrollTop = bestScrollTop ;
382
383
384
+ this . setState ( {
385
+ status : 'MEASURE_START' ,
386
+ scrollTop : bestScrollTop ,
387
+ itemIndex : bestItemIndex ,
388
+ itemOffsetPtg : bestItemOffsetPtg ,
389
+ startIndex : bestStartIndex ,
390
+ endIndex : bestEndIndex ,
391
+ } ) ;
392
+
393
+ requestAnimationFrame ( ( ) => {
383
394
requestAnimationFrame ( ( ) => {
384
- requestAnimationFrame ( ( ) => {
385
- this . lockScroll = false ;
386
- } ) ;
395
+ this . lockScroll = false ;
387
396
} ) ;
388
- }
397
+ } ) ;
389
398
}
390
399
}
391
400
0 commit comments