From 221e2fe4095bc9ae15878725bdac4071d53e61f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 26 Dec 2018 10:32:07 -0800 Subject: [PATCH] Remove mixins from ScrollView (#22374) Summary: As a part of https://github.com/facebook/react-native/pull/22301 it turned out that we need to first convert `ScrollView` to class component. As a first step to do so, here's removal of using `mixins` API, in favor of populating `_scrollResponder` field with `ScrollResponder.Mixin` (still used) methods. Pull Request resolved: https://github.com/facebook/react-native/pull/22374 Reviewed By: TheSavior Differential Revision: D13307775 Pulled By: RSNara fbshipit-source-id: 16be1df8a0bf9ccc5cc32f3a017a1279f99268ed --- Libraries/Components/ScrollView/ScrollView.js | 197 ++++++++++-------- 1 file changed, 111 insertions(+), 86 deletions(-) diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index e6b8c5c27313ed..1aaf8f2d3b7110 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -20,7 +20,6 @@ const StyleSheet = require('StyleSheet'); const View = require('View'); const InternalScrollViewType = require('InternalScrollViewType'); -const createReactClass = require('create-react-class'); const dismissKeyboard = require('dismissKeyboard'); const flattenStyle = require('flattenStyle'); const invariant = require('invariant'); @@ -522,6 +521,10 @@ export type Props = $ReadOnly<{| children?: React.Node, |}>; +type State = {| + layoutHeight: ?number, +|}; + /** * Component that wraps platform ScrollView while providing * integration with touch locking "responder" system. @@ -557,24 +560,31 @@ export type Props = $ReadOnly<{| * multiple columns, infinite scroll loading, or any number of other features it * supports out of the box. */ -const ScrollView = createReactClass({ - displayName: 'ScrollView', - mixins: [ScrollResponder.Mixin], - - _scrollAnimatedValue: (new AnimatedImplementation.Value( +class ScrollView extends React.Component { + _createScrollResponder = () => { + const scrollResponder = {...ScrollResponder.Mixin}; + for (const key in ScrollResponder.Mixin) { + if (typeof scrollResponder[key] === 'function') { + scrollResponder[key] = scrollResponder[key].bind(this); + (this: any)[key] = scrollResponder[key].bind(this); + } + } + return scrollResponder; + }; + _scrollResponder = this._createScrollResponder(); + _scrollAnimatedValue: AnimatedImplementation.Value = new AnimatedImplementation.Value( 0, - ): AnimatedImplementation.Value), - _scrollAnimatedValueAttachment: (null: ?{detach: () => void}), - _stickyHeaderRefs: (new Map(): Map), - _headerLayoutYs: (new Map(): Map), - getInitialState: function() { - return { - ...this.scrollResponderMixinGetInitialState(), - layoutHeight: null, - }; - }, + ); + _scrollAnimatedValueAttachment: ?{detach: () => void} = null; + _stickyHeaderRefs: Map = new Map(); + _headerLayoutYs: Map = new Map(); - UNSAFE_componentWillMount: function() { + state = { + layoutHeight: null, + }; + + UNSAFE_componentWillMount() { + this._scrollResponder.UNSAFE_componentWillMount(); this._scrollAnimatedValue = new AnimatedImplementation.Value( this.props.contentOffset ? this.props.contentOffset.y : 0, ); @@ -583,25 +593,26 @@ const ScrollView = createReactClass({ ); this._stickyHeaderRefs = new Map(); this._headerLayoutYs = new Map(); - }, + } - componentDidMount: function() { + componentDidMount() { this._updateAnimatedNodeAttachment(); - }, + } - componentDidUpdate: function() { + componentDidUpdate() { this._updateAnimatedNodeAttachment(); - }, + } - componentWillUnmount: function() { + componentWillUnmount() { + this._scrollResponder.componentWillUnmount(); if (this._scrollAnimatedValueAttachment) { this._scrollAnimatedValueAttachment.detach(); } - }, + } - setNativeProps: function(props: Object) { + setNativeProps = (props: Object) => { this._scrollViewRef && this._scrollViewRef.setNativeProps(props); - }, + }; /** * Returns a reference to the underlying scroll responder, which supports @@ -609,17 +620,17 @@ const ScrollView = createReactClass({ * implement this method so that they can be composed while providing access * to the underlying scroll responder's methods. */ - getScrollResponder: function(): ScrollView { + getScrollResponder = (): ScrollView => { return this; - }, + }; - getScrollableNode: function(): any { + getScrollableNode = (): any => { return ReactNative.findNodeHandle(this._scrollViewRef); - }, + }; - getInnerViewNode: function(): any { + getInnerViewNode = (): any => { return ReactNative.findNodeHandle(this._innerViewRef); - }, + }; /** * Scrolls to a given x, y offset, either immediately or with a smooth animation. @@ -632,11 +643,11 @@ const ScrollView = createReactClass({ * the function also accepts separate arguments as an alternative to the options object. * This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED. */ - scrollTo: function( + scrollTo = ( y?: number | {x?: number, y?: number, animated?: boolean}, x?: number, animated?: boolean, - ) { + ) => { if (typeof y === 'number') { console.warn( '`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, ' + @@ -645,12 +656,12 @@ const ScrollView = createReactClass({ } else { ({x, y, animated} = y || {}); } - this.getScrollResponder().scrollResponderScrollTo({ + this._scrollResponder.scrollResponderScrollTo({ x: x || 0, y: y || 0, animated: animated !== false, }); - }, + }; /** * If this is a vertical ScrollView scrolls to the bottom. @@ -660,40 +671,40 @@ const ScrollView = createReactClass({ * `scrollToEnd({animated: false})` for immediate scrolling. * If no options are passed, `animated` defaults to true. */ - scrollToEnd: function(options?: {animated?: boolean}) { + scrollToEnd = (options?: {animated?: boolean}) => { // Default to true const animated = (options && options.animated) !== false; - this.getScrollResponder().scrollResponderScrollToEnd({ + this._scrollResponder.scrollResponderScrollToEnd({ animated: animated, }); - }, + }; /** * Deprecated, use `scrollTo` instead. */ - scrollWithoutAnimationTo: function(y: number = 0, x: number = 0) { + scrollWithoutAnimationTo = (y: number = 0, x: number = 0) => { console.warn( '`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead', ); this.scrollTo({x, y, animated: false}); - }, + }; /** * Displays the scroll indicators momentarily. * * @platform ios */ - flashScrollIndicators: function() { - this.getScrollResponder().scrollResponderFlashScrollIndicators(); - }, + flashScrollIndicators = () => { + this._scrollResponder.scrollResponderFlashScrollIndicators(); + }; - _getKeyForIndex: function(index, childArray) { + _getKeyForIndex = (index, childArray) => { // $FlowFixMe Invalid prop usage const child = childArray[index]; return child && child.key; - }, + }; - _updateAnimatedNodeAttachment: function() { + _updateAnimatedNodeAttachment = () => { if (this._scrollAnimatedValueAttachment) { this._scrollAnimatedValueAttachment.detach(); } @@ -707,17 +718,17 @@ const ScrollView = createReactClass({ [{nativeEvent: {contentOffset: {y: this._scrollAnimatedValue}}}], ); } - }, + }; - _setStickyHeaderRef: function(key, ref) { + _setStickyHeaderRef = (key, ref) => { if (ref) { this._stickyHeaderRefs.set(key, ref); } else { this._stickyHeaderRefs.delete(key); } - }, + }; - _onStickyHeaderLayout: function(index, event, key) { + _onStickyHeaderLayout = (index, event, key) => { if (!this.props.stickyHeaderIndices) { return; } @@ -730,8 +741,10 @@ const ScrollView = createReactClass({ const layoutY = event.nativeEvent.layout.y; this._headerLayoutYs.set(key, layoutY); + // $FlowFixMe const indexOfIndex = this.props.stickyHeaderIndices.indexOf(index); const previousHeaderIndex = this.props.stickyHeaderIndices[ + // $FlowFixMe indexOfIndex - 1 ]; if (previousHeaderIndex != null) { @@ -740,9 +753,9 @@ const ScrollView = createReactClass({ ); previousHeader && previousHeader.setNextHeaderY(layoutY); } - }, + }; - _handleScroll: function(e: Object) { + _handleScroll = (e: Object) => { if (__DEV__) { if ( this.props.onScroll && @@ -761,40 +774,40 @@ const ScrollView = createReactClass({ if (Platform.OS === 'android') { if ( this.props.keyboardDismissMode === 'on-drag' && - this.state.isTouching + this._scrollResponder.isTouching ) { dismissKeyboard(); } } - this.scrollResponderHandleScroll(e); - }, + this._scrollResponder.scrollResponderHandleScroll(e); + }; - _handleLayout: function(e: Object) { + _handleLayout = (e: Object) => { if (this.props.invertStickyHeaders) { this.setState({layoutHeight: e.nativeEvent.layout.height}); } if (this.props.onLayout) { this.props.onLayout(e); } - }, + }; - _handleContentOnLayout: function(e: Object) { + _handleContentOnLayout = (e: Object) => { const {width, height} = e.nativeEvent.layout; this.props.onContentSizeChange && this.props.onContentSizeChange(width, height); - }, + }; - _scrollViewRef: (null: ?ScrollView), - _setScrollViewRef: function(ref: ?ScrollView) { + _scrollViewRef: ?ScrollView = null; + _setScrollViewRef = (ref: ?ScrollView) => { this._scrollViewRef = ref; - }, + }; - _innerViewRef: (null: ?NativeMethodsMixinType), - _setInnerViewRef: function(ref: ?NativeMethodsMixinType) { + _innerViewRef: ?NativeMethodsMixinType = null; + _setInnerViewRef = (ref: ?NativeMethodsMixinType) => { this._innerViewRef = ref; - }, + }; - render: function() { + render() { let ScrollViewClass; let ScrollContentContainerViewClass; if (Platform.OS === 'android') { @@ -852,9 +865,11 @@ const ScrollView = createReactClass({ const children = hasStickyHeaders ? // $FlowFixMe Invalid prop usage childArray.map((child, index) => { + // $FlowFixMe const indexOfIndex = child ? stickyHeaderIndices.indexOf(index) : -1; if (indexOfIndex > -1) { const key = child.key; + // $FlowFixMe const nextIndex = stickyHeaderIndices[indexOfIndex + 1]; return ( {Platform.isTV ? null : refreshControl} {contentContainer} @@ -1000,6 +1023,7 @@ const ScrollView = createReactClass({ {contentContainer} , @@ -1007,12 +1031,13 @@ const ScrollView = createReactClass({ } } return ( + // $FlowFixMe {contentContainer} ); - }, -}); + } +} const TypedScrollView = ((ScrollView: any): Class< InternalScrollViewType,