1313
1414const Batchinator = require ( 'Batchinator' ) ;
1515const FillRateHelper = require ( 'FillRateHelper' ) ;
16+ const PropTypes = require ( 'prop-types' ) ;
1617const React = require ( 'React' ) ;
1718const ReactNative = require ( 'ReactNative' ) ;
1819const RefreshControl = require ( 'RefreshControl' ) ;
@@ -139,7 +140,7 @@ type OptionalProps = {
139140 /**
140141 * Render a custom scroll component, e.g. with a differently styled `RefreshControl`.
141142 */
142- renderScrollComponent : ( props : Object ) => React . Element < any > ,
143+ renderScrollComponent ? : ( props : Object ) => React . Element < any > ,
143144 /**
144145 * Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off
145146 * screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`.
@@ -301,35 +302,32 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
301302 } ,
302303 maxToRenderPerBatch : 10 ,
303304 onEndReachedThreshold : 2 , // multiples of length
304- renderScrollComponent : ( props : Props ) => {
305- if ( props . onRefresh ) {
306- invariant (
307- typeof props . refreshing === 'boolean' ,
308- '`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `' +
309- JSON . stringify ( props . refreshing ) + '`' ,
310- ) ;
311-
312- return (
313- < ScrollView
314- { ...props }
315- refreshControl = {
316- < RefreshControl
317- refreshing = { props . refreshing }
318- onRefresh = { props . onRefresh }
319- progressViewOffset = { props . progressViewOffset }
320- />
321- }
322- />
323- ) ;
324- } else {
325- return < ScrollView { ...props } /> ;
326- }
327- } ,
328305 scrollEventThrottle : 50 ,
329306 updateCellsBatchingPeriod : 50 ,
330307 windowSize : 21 , // multiples of length
331308 } ;
332309
310+ static contextTypes = {
311+ virtualizedList : PropTypes . shape ( {
312+ horizontal : PropTypes . bool ,
313+ } ) ,
314+ } ;
315+
316+ static childContextTypes = {
317+ virtualizedList : PropTypes . shape ( {
318+ horizontal : PropTypes . bool ,
319+ } ) ,
320+ } ;
321+
322+ getChildContext ( ) {
323+ return {
324+ virtualizedList : {
325+ horizontal : this . props . horizontal ,
326+ // TODO: support nested virtualization and onViewableItemsChanged
327+ } ,
328+ } ;
329+ }
330+
333331 state: State ;
334332
335333 constructor ( props : Props , context : Object ) {
@@ -339,6 +337,11 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
339337 'Components based on VirtualizedList must be wrapped with Animated.createAnimatedComponent ' +
340338 'to support native onScroll events with useNativeDriver' ,
341339 ) ;
340+ invariant (
341+ ! ( this . _isNestedWithSameOrientation ( ) && props . onViewableItemsChanged ) ,
342+ 'Nesting lists that scroll in the same direction does not support onViewableItemsChanged' +
343+ 'on the inner list.'
344+ ) ;
342345
343346 this . _fillRateHelper = new FillRateHelper ( this . _getFrameMetrics ) ;
344347 this . _updateCellsToRenderBatcher = new Batchinator (
@@ -431,6 +434,15 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
431434 } ) ;
432435 } ;
433436
437+ _isVirtualizationDisabled ( ) : bool {
438+ return this . props . disableVirtualization || this . _isNestedWithSameOrientation ( ) ;
439+ }
440+
441+ _isNestedWithSameOrientation ( ) : bool {
442+ const nestedContext = this . context . virtualizedList ;
443+ return ! ! ( nestedContext && ! ! nestedContext . horizontal === ! ! this . props . horizontal ) ;
444+ }
445+
434446 render ( ) {
435447 if ( __DEV__ ) {
436448 const flatStyles = flattenStyle ( this . props . contentContainerStyle ) ;
@@ -442,7 +454,8 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
442454 }
443455
444456 const { ListEmptyComponent, ListFooterComponent, ListHeaderComponent} = this . props ;
445- const { data, disableVirtualization, horizontal} = this . props ;
457+ const { data, horizontal} = this . props ;
458+ const isVirtualizationDisabled = this . _isVirtualizationDisabled ( ) ;
446459 const cells = [ ] ;
447460 const stickyIndicesFromProps = new Set ( this . props . stickyHeaderIndices ) ;
448461 const stickyHeaderIndices = [ ] ;
@@ -466,7 +479,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
466479 const { first, last} = this . state ;
467480 this . _pushCells ( cells , stickyHeaderIndices , stickyIndicesFromProps , 0 , lastInitialIndex ) ;
468481 const firstAfterInitial = Math . max ( lastInitialIndex + 1 , first ) ;
469- if ( ! disableVirtualization && first > lastInitialIndex + 1 ) {
482+ if ( ! isVirtualizationDisabled && first > lastInitialIndex + 1 ) {
470483 let insertedStickySpacer = false ;
471484 if ( stickyIndicesFromProps . size > 0 ) {
472485 const stickyOffset = ListHeaderComponent ? 1 : 0 ;
@@ -507,7 +520,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
507520 ) ;
508521 this . _hasWarned . keys = true ;
509522 }
510- if ( ! disableVirtualization && last < itemCount - 1 ) {
523+ if ( ! isVirtualizationDisabled && last < itemCount - 1 ) {
511524 const lastFrame = this . _getFrameMetricsApprox ( last ) ;
512525 // Without getItemLayout, we limit our tail spacer to the _highestMeasuredFrameIndex to
513526 // prevent the user for hyperscrolling into un-measured area because otherwise content will
@@ -543,18 +556,21 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
543556 </ View >
544557 ) ;
545558 }
559+ const scrollProps = {
560+ ...this . props ,
561+ onContentSizeChange : this . _onContentSizeChange ,
562+ onLayout : this . _onLayout ,
563+ onScroll : this . _onScroll ,
564+ onScrollBeginDrag : this . _onScrollBeginDrag ,
565+ onScrollEndDrag : this . _onScrollEndDrag ,
566+ onMomentumScrollEnd : this . _onMomentumScrollEnd ,
567+ scrollEventThrottle : this . props . scrollEventThrottle , // TODO: Android support
568+ stickyHeaderIndices,
569+ } ;
546570 const ret = React . cloneElement (
547- this . props . renderScrollComponent ( this . props ) ,
571+ ( this . props . renderScrollComponent || this . _defaultRenderScrollComponent ) ( scrollProps ) ,
548572 {
549- onContentSizeChange : this . _onContentSizeChange ,
550- onLayout : this . _onLayout ,
551- onScroll : this . _onScroll ,
552- onScrollBeginDrag : this . _onScrollBeginDrag ,
553- onScrollEndDrag : this . _onScrollEndDrag ,
554- onMomentumScrollEnd : this . _onMomentumScrollEnd ,
555573 ref : this . _captureScrollRef ,
556- scrollEventThrottle : this . props . scrollEventThrottle , // TODO: Android support
557- stickyHeaderIndices,
558574 } ,
559575 cells ,
560576 ) ;
@@ -601,6 +617,32 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
601617 ) ;
602618 }
603619
620+ _defaultRenderScrollComponent = ( props ) => {
621+ if ( this . _isNestedWithSameOrientation ( ) ) {
622+ return < View { ...props } /> ;
623+ } else if ( props . onRefresh ) {
624+ invariant (
625+ typeof props . refreshing === 'boolean' ,
626+ '`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `' +
627+ JSON . stringify ( props . refreshing ) + '`' ,
628+ ) ;
629+ return (
630+ < ScrollView
631+ { ...props }
632+ refreshControl = {
633+ < RefreshControl
634+ refreshing = { props . refreshing }
635+ onRefresh = { props . onRefresh }
636+ progressViewOffset = { props . progressViewOffset }
637+ />
638+ }
639+ />
640+ ) ;
641+ } else {
642+ return < ScrollView { ...props } /> ;
643+ }
644+ } ;
645+
604646 _onCellLayout ( e , cellKey , index ) {
605647 const layout = e . nativeEvent . layout ;
606648 const next = {
@@ -816,14 +858,15 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
816858 } ;
817859
818860 _updateCellsToRender = ( ) => {
819- const { data, disableVirtualization, getItemCount, onEndReachedThreshold} = this . props ;
861+ const { data, getItemCount, onEndReachedThreshold} = this . props ;
862+ const isVirtualizationDisabled = this . _isVirtualizationDisabled ( ) ;
820863 this . _updateViewableItems ( data ) ;
821864 if ( ! data ) {
822865 return ;
823866 }
824867 this . setState ( ( state ) => {
825868 let newState ;
826- if ( ! disableVirtualization ) {
869+ if ( ! isVirtualizationDisabled ) {
827870 newState = computeWindowedRenderLimits (
828871 this . props , state , this . _getFrameMetricsApprox , this . _scrollMetrics ,
829872 ) ;
0 commit comments