Reputation: 333
I am trying to use a horizontal ScrollView in React Native for Android, where the starting position is in the middle of the scrolling images rather than (0,0).
The scrollTo
method seems to be called correctly inside componentDidMount
but nothing moves in the application, it still shows as starting the scroll all the way to the left.
Since this is Android I don't have access to contentOffset props or I would set that directly, according to the documentation. Here is the code:
'use strict';
var React = require('react-native');
var {
StyleSheet,
View,
Text,
ScrollView,
Component,
} = React;
var precomputeStyle = require('precomputeStyle');
class Carousel extends Component {
constructor(props, context) {
super(props, context);
//this.changeContent = this.changeContent.bind(this);
}
componentDidMount() {
this.myScroll.scrollTo(100);
console.log("called DidMount");
}
render() {
return (
<View style={{flex: 1}}>
<ScrollView ref={(ref) => this.myScroll = ref}
contentContainerStyle={styles.container}
horizontal={true}
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
bounces={true}
onMomentumScrollEnd={this.onAnimationEnd}
>
{this.props.children}
</ScrollView>
</View>
);
}
onAnimationEnd(e) {
console.log("curr offset: " + e.nativeEvent.contentOffset.x);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
page: {
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
},
});
module.exports = Carousel;
Upvotes: 33
Views: 32709
Reputation: 470
const [listIndex, setListIndex] = useState(inqId?list.findIndex(obj =>
obj.InqID == inqId):null);
const [ref, setRef] = useState(null)
scrollToPosition = () => {
listRef.scrollTo({ y: 50*ref });
}
<ScrollView
ref={(ref) => { setRef(ref) }}
onLayout={scrollToPosition}
/>
Upvotes: -1
Reputation: 3633
I think there should be a more modern, hooks version of this:
const MyComponent = (props) => {
const componentRef = useRef(null)
// this is the same as useEffect but only runs after finishing rendering
useLayoutEffect(() => {
// unlike class examples, the actual ref is stored in the current property
scrollViewRef.current.scrollTo({ y: 100 })
// this empty because we don't care about any other variables,
// if you add other stuff into this function,
// you'll have to add any hook based variables into this array
}, [])
// ditto about current here:
return (
<ScrollView ref={(ref) => (componentRef.current = ref)}>
{...}
</ScrollView>)
}
Upvotes: 0
Reputation: 453
Thanks @David Nathan, using InteractionManager
works for me.
also note that unlike setTimeout
, runAfterInteractions
will not delay active animations.
Upvotes: -1
Reputation: 331
I wanted to avoid using delays and timers so after a bit of digging I found that using onLayout
works very smooth:
scrollToInitialPosition = () => {
this.scrollViewRef.scrollTo({ y: 100 });
}
...
<ScrollView
ref={(ref) => { this.scrollViewRef = ref; }}
onLayout={this.scrollToInitialPosition}
/>
Upvotes: 33
Reputation: 4394
This works on React Native 0.44.0. Thanks for the hint @Eldelshell. It also seems to work with any timeout value. At least on the emulator. I found that the answer involving InteractionManager.runAfterInteractions
did nothing to fix the issue but perhaps that's a difference in versions.
componentDidMount() {
setTimeout(() => {
this._scroll.scrollTo({y: 100})
}, 1)
}
Upvotes: 25
Reputation: 536
I had the same issue, and wasted several hours no it:
1: in android, ScrollView can scroll only when its size < content's size
2: in react native android, if you call ScrollView.scrollTo() in componentDidMount, it won't work, because ScrollView has a layout animation when create, you can find it in ReactScrollView.java
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Call with the present values in order to re-layout if necessary
scrollTo(getScrollX(), getScrollY());
}
so, you must delay it after the animation
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.myScroll.scrollTo(100);
console.log("called DidMount");
})
}
Upvotes: 52