diff --git a/packages/react-native-web/src/exports/ScrollView/__tests__/index-test.js b/packages/react-native-web/src/exports/ScrollView/__tests__/index-test.js
index 8a93d818..f93e3c54 100644
--- a/packages/react-native-web/src/exports/ScrollView/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/ScrollView/__tests__/index-test.js
@@ -38,4 +38,52 @@ describe('components/ScrollView', () => {
expect(onScroll).not.toBeCalled();
});
});
+
+ describe('prop "ref"', () => {
+ test('value is set', () => {
+ const ref = jest.fn();
+ render();
+ expect(ref).toBeCalled();
+ });
+
+ test('is not called for prop changes', () => {
+ const ref = jest.fn();
+ let rerender;
+ act(() => {
+ ({ rerender } = render());
+ });
+ expect(ref).toHaveBeenCalledTimes(1);
+ act(() => {
+ rerender();
+ });
+ expect(ref).toHaveBeenCalledTimes(1);
+ });
+
+ test('node has imperative methods', () => {
+ const ref = React.createRef();
+ act(() => {
+ render();
+ });
+ const node = ref.current;
+
+ // Did we get an HTMLElement?
+ expect(node.tagName === 'DIV').toBe(true);
+ // Does it have the "platform" methods?
+ expect(typeof node.measure === 'function').toBe(true);
+ expect(typeof node.measureLayout === 'function').toBe(true);
+ expect(typeof node.measureInWindow === 'function').toBe(true);
+ expect(typeof node.setNativeProps === 'function').toBe(true);
+ // Does it have the scrollview methods?
+ expect(typeof node.getScrollResponder === 'function').toBe(true);
+ expect(typeof node.getScrollableNode === 'function').toBe(true);
+ expect(typeof node.getInnerViewNode === 'function').toBe(true);
+ expect(typeof node.getInnerViewRef === 'function').toBe(true);
+ expect(typeof node.getNativeScrollRef === 'function').toBe(true);
+ expect(typeof node.scrollTo === 'function').toBe(true);
+ expect(typeof node.scrollToEnd === 'function').toBe(true);
+ expect(typeof node.flashScrollIndicators === 'function').toBe(true);
+ expect(typeof node.scrollResponderZoomTo === 'function').toBe(true);
+ expect(typeof node.scrollResponderScrollNativeHandleToKeyboard === 'function').toBe(true);
+ });
+ });
});
diff --git a/packages/react-native-web/src/exports/ScrollView/index.js b/packages/react-native-web/src/exports/ScrollView/index.js
index 4a826dfb..362da490 100644
--- a/packages/react-native-web/src/exports/ScrollView/index.js
+++ b/packages/react-native-web/src/exports/ScrollView/index.js
@@ -13,6 +13,7 @@ import type { ViewProps, ViewStyle } from '../View/types';
import createReactClass from 'create-react-class';
import dismissKeyboard from '../../modules/dismissKeyboard';
import invariant from 'fbjs/lib/invariant';
+import mergeRefs from '../../modules/mergeRefs';
import ScrollResponder from '../../modules/ScrollResponder';
import ScrollViewBase from './ScrollViewBase';
import StyleSheet from '../StyleSheet';
@@ -47,12 +48,6 @@ const ScrollView = ((createReactClass({
this.scrollResponderFlashScrollIndicators();
},
- setNativeProps(props: Object) {
- if (this._scrollNodeRef) {
- this._scrollNodeRef.setNativeProps(props);
- }
- },
-
/**
* Returns a reference to the underlying scroll responder, which supports
* operations like `scrollTo`. All ScrollView-like components should
@@ -67,10 +62,18 @@ const ScrollView = ((createReactClass({
return this._scrollNodeRef;
},
+ getInnerViewRef(): any {
+ return this._innerViewRef;
+ },
+
getInnerViewNode(): any {
return this._innerViewRef;
},
+ getNativeScrollRef(): any {
+ return this._scrollNodeRef;
+ },
+
/**
* Scrolls to a given x, y offset, either immediately or with a smooth animation.
* Syntax:
@@ -129,6 +132,7 @@ const ScrollView = ((createReactClass({
stickyHeaderIndices,
pagingEnabled,
/* eslint-disable */
+ forwardedRef,
keyboardDismissMode,
onScroll,
/* eslint-enable */
@@ -261,12 +265,29 @@ const ScrollView = ((createReactClass({
this.scrollResponderHandleScroll(e);
},
- _setInnerViewRef(component) {
- this._innerViewRef = component;
+ _setInnerViewRef(node) {
+ this._innerViewRef = node;
},
- _setScrollNodeRef(component) {
- this._scrollNodeRef = component;
+ _setScrollNodeRef(node) {
+ this._scrollNodeRef = node;
+ // ScrollView needs to add more methods to the hostNode in addition to those
+ // added by `usePlatformMethods`. This is temporarily until an API like
+ // `ScrollView.scrollTo(hostNode, { x, y })` is added to React Native.
+ if (node != null) {
+ node.getScrollResponder = this.getScrollResponder;
+ node.getInnerViewNode = this.getInnerViewNode;
+ node.getInnerViewRef = this.getInnerViewRef;
+ node.getNativeScrollRef = this.getNativeScrollRef;
+ node.getScrollableNode = this.getScrollableNode;
+ node.scrollTo = this.scrollTo;
+ node.scrollToEnd = this.scrollToEnd;
+ node.flashScrollIndicators = this.flashScrollIndicators;
+ node.scrollResponderZoomTo = this.scrollResponderZoomTo;
+ node.scrollResponderScrollNativeHandleToKeyboard = this.scrollResponderScrollNativeHandleToKeyboard;
+ }
+ const ref = mergeRefs(this.props.forwardedRef);
+ ref(node);
}
}): any): React.ComponentType);
@@ -313,4 +334,10 @@ const styles = StyleSheet.create({
}
});
-export default ScrollView;
+const ForwardedScrollView = React.forwardRef((props, forwardedRef) => {
+ return ;
+});
+
+ForwardedScrollView.displayName = 'ScrollView';
+
+export default ForwardedScrollView;