mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-23 14:57:13 +00:00
[fix] ScrollView ref and methods
Make sure the 'ref' for a ScrollView is a host node with React Native's proprietary and legacy instance methods attached. Fix #1957
This commit is contained in:
@@ -38,4 +38,52 @@ describe('components/ScrollView', () => {
|
||||
expect(onScroll).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prop "ref"', () => {
|
||||
test('value is set', () => {
|
||||
const ref = jest.fn();
|
||||
render(<ScrollView ref={ref} />);
|
||||
expect(ref).toBeCalled();
|
||||
});
|
||||
|
||||
test('is not called for prop changes', () => {
|
||||
const ref = jest.fn();
|
||||
let rerender;
|
||||
act(() => {
|
||||
({ rerender } = render(<ScrollView nativeID="123" ref={ref} style={{ borderWidth: 5 }} />));
|
||||
});
|
||||
expect(ref).toHaveBeenCalledTimes(1);
|
||||
act(() => {
|
||||
rerender(<ScrollView nativeID="1234" ref={ref} style={{ borderWidth: 6 }} />);
|
||||
});
|
||||
expect(ref).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('node has imperative methods', () => {
|
||||
const ref = React.createRef();
|
||||
act(() => {
|
||||
render(<ScrollView ref={ref} />);
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+38
-11
@@ -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<ScrollViewProps>);
|
||||
|
||||
@@ -313,4 +334,10 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
});
|
||||
|
||||
export default ScrollView;
|
||||
const ForwardedScrollView = React.forwardRef((props, forwardedRef) => {
|
||||
return <ScrollView {...props} forwardedRef={forwardedRef} />;
|
||||
});
|
||||
|
||||
ForwardedScrollView.displayName = 'ScrollView';
|
||||
|
||||
export default ForwardedScrollView;
|
||||
|
||||
Reference in New Issue
Block a user