@@ -35,6 +35,7 @@ import type {ViewProps} from 'ViewPropTypes';
35
35
import type { PointProp } from 'PointPropType' ;
36
36
37
37
import type { ColorValue } from 'StyleSheetTypes' ;
38
+ import type { State as ScrollResponderState } from 'ScrollResponder' ;
38
39
39
40
let AndroidScrollView ;
40
41
let AndroidHorizontalScrollContentView ;
@@ -523,8 +524,23 @@ export type Props = $ReadOnly<{|
523
524
524
525
type State = { |
525
526
layoutHeight : ?number ,
527
+ ...ScrollResponderState ,
526
528
| } ;
527
529
530
+ function createScrollResponder (
531
+ node : React . ElementRef < typeof ScrollView > ,
532
+ ) : typeof ScrollResponder . Mixin {
533
+ const scrollResponder = { ...ScrollResponder . Mixin } ;
534
+
535
+ for ( const key in scrollResponder ) {
536
+ if ( typeof scrollResponder [ key ] === 'function' ) {
537
+ scrollResponder [ key ] = scrollResponder [ key ] . bind ( node ) ;
538
+ }
539
+ }
540
+
541
+ return scrollResponder ;
542
+ }
543
+
528
544
/**
529
545
* Component that wraps platform ScrollView while providing
530
546
* integration with touch locking "responder" system.
@@ -561,17 +577,52 @@ type State = {|
561
577
* supports out of the box.
562
578
*/
563
579
class ScrollView extends React . Component < Props , State > {
564
- _createScrollResponder = ( ) => {
565
- const scrollResponder = { ...ScrollResponder . Mixin } ;
580
+ /**
581
+ * Part 1: Removing ScrollResponder.Mixin:
582
+ *
583
+ * 1. Mixin methods should be flow typed. That's why we create a
584
+ * copy of ScrollResponder.Mixin and attach it to this._scrollResponder.
585
+ * Otherwise, we'd have to manually declare each method on the component
586
+ * class and assign it a flow type.
587
+ * 2. Mixin methods can call component methods, and access the component's
588
+ * props and state. So, we need to bind all mixin methods to the
589
+ * component instance.
590
+ * 3. Continued...
591
+ */
592
+ _scrollResponder : typeof ScrollResponder . Mixin = createScrollResponder ( this ) ;
593
+
594
+ constructor ( ...args ) {
595
+ super ( ...args ) ;
596
+
597
+ /**
598
+ * Part 2: Removing ScrollResponder.Mixin
599
+ *
600
+ * 3. Mixin methods access other mixin methods via dynamic dispatch using
601
+ * this. Since mixin methods are bound to the component instance, we need
602
+ * to copy all mixin methods to the component instance.
603
+ */
566
604
for ( const key in ScrollResponder . Mixin ) {
567
- if ( typeof scrollResponder [ key ] === 'function' ) {
568
- scrollResponder [ key ] = scrollResponder [ key ] . bind ( this ) ;
569
- ( this : any ) [ key ] = scrollResponder [ key ] . bind ( this ) ;
605
+ if (
606
+ typeof ScrollResponder . Mixin [ key ] === 'function' &&
607
+ key . startsWith ( 'scrollResponder' )
608
+ ) {
609
+ ( this : any ) [ key ] = ScrollResponder . Mixin [ key ] . bind ( this ) ;
570
610
}
571
611
}
572
- return scrollResponder ;
573
- } ;
574
- _scrollResponder = this . _createScrollResponder ( ) ;
612
+
613
+ /**
614
+ * Part 3: Removing ScrollResponder.Mixin
615
+ *
616
+ * 4. Mixins can initialize properties and use properties on the component
617
+ * instance.
618
+ */
619
+ Object . keys ( ScrollResponder . Mixin )
620
+ . filter ( key => typeof ScrollResponder . Mixin [ key ] !== 'function' )
621
+ . forEach ( key => {
622
+ ( this : any ) [ key ] = ScrollResponder . Mixin [ key ] ;
623
+ } ) ;
624
+ }
625
+
575
626
_scrollAnimatedValue : AnimatedImplementation . Value = new AnimatedImplementation . Value (
576
627
0 ,
577
628
) ;
@@ -581,6 +632,7 @@ class ScrollView extends React.Component<Props, State> {
581
632
582
633
state = {
583
634
layoutHeight : null ,
635
+ ...ScrollResponder . Mixin . scrollResponderMixinGetInitialState ( ) ,
584
636
} ;
585
637
586
638
UNSAFE_componentWillMount ( ) {
@@ -610,27 +662,27 @@ class ScrollView extends React.Component<Props, State> {
610
662
}
611
663
}
612
664
613
- setNativeProps = ( props : Object ) = > {
665
+ setNativeProps ( props : Object ) {
614
666
this . _scrollViewRef && this . _scrollViewRef . setNativeProps ( props ) ;
615
- } ;
667
+ }
616
668
617
669
/**
618
670
* Returns a reference to the underlying scroll responder, which supports
619
671
* operations like `scrollTo`. All ScrollView-like components should
620
672
* implement this method so that they can be composed while providing access
621
673
* to the underlying scroll responder's methods.
622
674
*/
623
- getScrollResponder = ( ) : ScrollView => {
675
+ getScrollResponder ( ) : ScrollView {
624
676
return this ;
625
- } ;
677
+ }
626
678
627
- getScrollableNode = ( ) : any => {
679
+ getScrollableNode ( ) : any {
628
680
return ReactNative . findNodeHandle ( this . _scrollViewRef ) ;
629
- } ;
681
+ }
630
682
631
- getInnerViewNode = ( ) : any => {
683
+ getInnerViewNode ( ) : any {
632
684
return ReactNative . findNodeHandle ( this . _innerViewRef ) ;
633
- } ;
685
+ }
634
686
635
687
/**
636
688
* Scrolls to a given x, y offset, either immediately or with a smooth animation.
@@ -643,11 +695,11 @@ class ScrollView extends React.Component<Props, State> {
643
695
* the function also accepts separate arguments as an alternative to the options object.
644
696
* This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED.
645
697
*/
646
- scrollTo = (
698
+ scrollTo (
647
699
y ?: number | { x ?: number , y ?: number , animated ?: boolean } ,
648
700
x ?: number ,
649
701
animated ? : boolean ,
650
- ) => {
702
+ ) {
651
703
if ( typeof y === 'number' ) {
652
704
console . warn (
653
705
'`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, ' +
@@ -661,7 +713,7 @@ class ScrollView extends React.Component<Props, State> {
661
713
y : y || 0 ,
662
714
animated : animated !== false ,
663
715
} ) ;
664
- } ;
716
+ }
665
717
666
718
/**
667
719
* If this is a vertical ScrollView scrolls to the bottom.
@@ -671,40 +723,40 @@ class ScrollView extends React.Component<Props, State> {
671
723
* `scrollToEnd({animated: false})` for immediate scrolling.
672
724
* If no options are passed, `animated` defaults to true.
673
725
*/
674
- scrollToEnd = ( options ?: { animated ?: boolean } ) => {
726
+ scrollToEnd ( options ?: { animated ?: boolean } ) {
675
727
// Default to true
676
728
const animated = ( options && options . animated ) !== false ;
677
729
this . _scrollResponder . scrollResponderScrollToEnd ( {
678
730
animated : animated ,
679
731
} ) ;
680
- } ;
732
+ }
681
733
682
734
/**
683
735
* Deprecated, use `scrollTo` instead.
684
736
*/
685
- scrollWithoutAnimationTo = ( y : number = 0 , x : number = 0 ) => {
737
+ scrollWithoutAnimationTo ( y : number = 0 , x : number = 0 ) {
686
738
console . warn (
687
739
'`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead' ,
688
740
) ;
689
741
this . scrollTo ( { x, y, animated : false } ) ;
690
- } ;
742
+ }
691
743
692
744
/**
693
745
* Displays the scroll indicators momentarily.
694
746
*
695
747
* @platform ios
696
748
*/
697
- flashScrollIndicators = ( ) = > {
749
+ flashScrollIndicators ( ) {
698
750
this . _scrollResponder . scrollResponderFlashScrollIndicators ( ) ;
699
- } ;
751
+ }
700
752
701
- _getKeyForIndex = ( index , childArray ) = > {
753
+ _getKeyForIndex ( index , childArray ) {
702
754
// $FlowFixMe Invalid prop usage
703
755
const child = childArray [ index ] ;
704
756
return child && child . key ;
705
- } ;
757
+ }
706
758
707
- _updateAnimatedNodeAttachment = ( ) => {
759
+ _updateAnimatedNodeAttachment ( ) {
708
760
if ( this . _scrollAnimatedValueAttachment ) {
709
761
this . _scrollAnimatedValueAttachment . detach ( ) ;
710
762
}
@@ -718,18 +770,19 @@ class ScrollView extends React.Component<Props, State> {
718
770
[ { nativeEvent : { contentOffset : { y : this . _scrollAnimatedValue } } } ] ,
719
771
) ;
720
772
}
721
- } ;
773
+ }
722
774
723
- _setStickyHeaderRef = ( key , ref ) = > {
775
+ _setStickyHeaderRef ( key , ref ) {
724
776
if ( ref ) {
725
777
this . _stickyHeaderRefs . set ( key , ref ) ;
726
778
} else {
727
779
this . _stickyHeaderRefs . delete ( key ) ;
728
780
}
729
- } ;
781
+ }
730
782
731
- _onStickyHeaderLayout = ( index , event , key ) = > {
732
- if ( ! this . props . stickyHeaderIndices ) {
783
+ _onStickyHeaderLayout ( index , event , key ) {
784
+ const { stickyHeaderIndices } = this . props ;
785
+ if ( ! stickyHeaderIndices ) {
733
786
return ;
734
787
}
735
788
const childArray = React . Children . toArray ( this . props . children ) ;
@@ -741,19 +794,15 @@ class ScrollView extends React.Component<Props, State> {
741
794
const layoutY = event . nativeEvent . layout . y ;
742
795
this . _headerLayoutYs . set ( key , layoutY ) ;
743
796
744
- // $FlowFixMe
745
- const indexOfIndex = this . props . stickyHeaderIndices . indexOf ( index ) ;
746
- const previousHeaderIndex = this . props . stickyHeaderIndices [
747
- // $FlowFixMe
748
- indexOfIndex - 1
749
- ] ;
797
+ const indexOfIndex = stickyHeaderIndices . indexOf ( index ) ;
798
+ const previousHeaderIndex = stickyHeaderIndices [ indexOfIndex - 1 ] ;
750
799
if ( previousHeaderIndex != null ) {
751
800
const previousHeader = this . _stickyHeaderRefs . get (
752
801
this . _getKeyForIndex ( previousHeaderIndex , childArray ) ,
753
802
) ;
754
803
previousHeader && previousHeader . setNextHeaderY ( layoutY ) ;
755
804
}
756
- } ;
805
+ }
757
806
758
807
_handleScroll = ( e : Object ) => {
759
808
if ( __DEV__ ) {
@@ -774,7 +823,7 @@ class ScrollView extends React.Component<Props, State> {
774
823
if ( Platform . OS === 'android' ) {
775
824
if (
776
825
this . props . keyboardDismissMode === 'on-drag' &&
777
- this . _scrollResponder . isTouching
826
+ this . state . isTouching
778
827
) {
779
828
dismissKeyboard ( ) ;
780
829
}
@@ -858,40 +907,39 @@ class ScrollView extends React.Component<Props, State> {
858
907
}
859
908
860
909
const { stickyHeaderIndices} = this . props ;
910
+ let children = this . props . children ;
911
+
912
+ if ( stickyHeaderIndices != null && stickyHeaderIndices . length > 0 ) {
913
+ const childArray = React . Children . toArray ( this . props . children ) ;
914
+
915
+ children = childArray . map ( ( child , index ) => {
916
+ const indexOfIndex = child ? stickyHeaderIndices . indexOf ( index ) : - 1 ;
917
+ if ( indexOfIndex > - 1 ) {
918
+ const key = child . key ;
919
+ const nextIndex = stickyHeaderIndices [ indexOfIndex + 1 ] ;
920
+ return (
921
+ < ScrollViewStickyHeader
922
+ key = { key }
923
+ ref = { ref => this . _setStickyHeaderRef ( key , ref ) }
924
+ nextHeaderLayoutY = { this . _headerLayoutYs . get (
925
+ this . _getKeyForIndex ( nextIndex , childArray ) ,
926
+ ) }
927
+ onLayout = { event => this . _onStickyHeaderLayout ( index , event , key ) }
928
+ scrollAnimatedValue = { this . _scrollAnimatedValue }
929
+ inverted = { this . props . invertStickyHeaders }
930
+ scrollViewHeight = { this . state . layoutHeight } >
931
+ { child }
932
+ </ ScrollViewStickyHeader >
933
+ ) ;
934
+ } else {
935
+ return child ;
936
+ }
937
+ } ) ;
938
+ }
939
+
861
940
const hasStickyHeaders =
862
941
stickyHeaderIndices && stickyHeaderIndices . length > 0 ;
863
- const childArray =
864
- hasStickyHeaders && React . Children . toArray ( this . props . children ) ;
865
- const children = hasStickyHeaders
866
- ? // $FlowFixMe Invalid prop usage
867
- childArray . map ( ( child , index ) => {
868
- // $FlowFixMe
869
- const indexOfIndex = child ? stickyHeaderIndices . indexOf ( index ) : - 1 ;
870
- if ( indexOfIndex > - 1 ) {
871
- const key = child . key ;
872
- // $FlowFixMe
873
- const nextIndex = stickyHeaderIndices [ indexOfIndex + 1 ] ;
874
- return (
875
- < ScrollViewStickyHeader
876
- key = { key }
877
- ref = { ref => this . _setStickyHeaderRef ( key , ref ) }
878
- nextHeaderLayoutY = { this . _headerLayoutYs . get (
879
- this . _getKeyForIndex ( nextIndex , childArray ) ,
880
- ) }
881
- onLayout = { event =>
882
- this . _onStickyHeaderLayout ( index , event , key )
883
- }
884
- scrollAnimatedValue = { this . _scrollAnimatedValue }
885
- inverted = { this . props . invertStickyHeaders }
886
- scrollViewHeight = { this . state . layoutHeight } >
887
- { child }
888
- </ ScrollViewStickyHeader >
889
- ) ;
890
- } else {
891
- return child ;
892
- }
893
- } )
894
- : this . props . children ;
942
+
895
943
const contentContainer = (
896
944
< ScrollContentContainerViewClass
897
945
{ ...contentSizeChangeProps }
0 commit comments