33

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;
3
  • 1
    Can you try adding a Y coordinate as well? E.g. scrollTo(100, 0). Commented Oct 19, 2015 at 10:15
  • I did and the problem stays the same, the missing coordinate just defaults to zero if not specified. Commented Oct 20, 2015 at 8:31
  • I think it's actually scrollTo(y, x) but that syntax is deprecated. It's now scrollTo({x: n, y: n, animated: bool}) and as Rob mentioned these are you can omit x, y and animated depending on your needs. Commented May 28, 2017 at 18:43

6 Answers 6

52

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");
    })  
}
Sign up to request clarification or add additional context in comments.

2 Comments

This solution works, but I had to add a delay inside this function: _.delay(() => this._scroll.scrollTo({x: 1000, animated: false}), 100);
Just using a delay without the interaction manager worked for me on a ScrollView. I'll post an answer with the code as I had trouble finding a good resource on this.
33

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}
/>

4 Comments

This should be selected as the best answer, notice that the answer selected as best is from year 2016.
This worked for me! I struggled for quite some time. Thanks!
Thanks dude ! This should be the accepted answer. Saved my time :)
Legendary stuff
25

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)
}

3 Comments

This answer assumes that the ScrollView's initial animation is finished within 1ms. This may happen most of the time, but it's not guaranteed. That's why InteractionManager.runAfterInteractions() is the preferred solution, and setTimeout() is somewhat sloppy.
@NathanK You didn't read Phil's answer. He clearly stated that using InteractionManager did not fix the problem for him. It didn't fix the problem for me either. The only solution I can find that fixes this issue is to use setTimeout with a low value.
Same for me it's not working with InteractionManager.runAfterInteractions but working with setTimeout
0

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>)

}

Comments

-1

Thanks @David Nathan, using InteractionManager works for me.
also note that unlike setTimeout , runAfterInteractions will not delay active animations.

From InteractionManager docs

Comments

-1
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}
/>

2 Comments

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
Why would you use useState if you have useRef?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.