Scroll to bottom of ScrollView in React Native

There's a better way to do this. In broad strokes:

Use a ListView to render the chat.

<ListView
  ref="list"
  ...

Add a footer to the list with renderFooter that uses onLayout to save its y position. The footer doesn't have to render anything visible, just an empty View. All you're looking for is where the bottom of the list is.

renderFooter={() => {
  return <View onLayout={(e)=> {
    this.footerY = e.nativeEvent.layout.y;
  }}/>
}},

Add an onLayout handler to the ListView itself that saves the lists height.

onLayout={(e)=>{
  this.listHeight = e.nativeEvent.layout.height;
}}

With those two bits of information you can scroll to the bottom of the list with something like:

if (this.listHeight && this.footerY && this.footerY > this.listHeight) {
  var scrollDistance = this.listHeight - this.footerY;
  var scrollResponder = this.refs.list.getScrollResponder();
  scrollResponder.scrollWithoutAnimationTo(-scrollDistance);
}

How often you scroll to the bottom is up to you and depends on the behavior you want.

An example of this in action is GiftedMessenger.


While RCTUIManager.measure() does work by extracting the 4th element of the data array via reference, it's much more appropriate ( and easier ) to use the following when working with ScrollViews:

RE: https://facebook.github.io/react-native/docs/scrollview.html#oncontentsizechange

onContentSizeChange={( contentWidth, contentHeight ) => {
    this._contentHeight = contentHeight
}}

This event will fire once the inner child components finish laying out and upon change. This works for me and I recommend this supported approach when determining content bounds.


It was pretty consealed and I ran into the source code.

You can try like this:

require:

var RCTUIManager = require('NativeModules').UIManager;

render:

<ScrollView ref = {component=>{this._scrollView=component;}}>
</ScrollView>

event:

someEvent: function() {
  RCTUIManager.measure(this._scrollView.getInnerViewNode(), (...data)=>{console.log(data)});
}

From the data, you can get the contentsize of the scrollview whose height is the fourth element of data. Of course you can get the content offset from it. Run into the source code of RCTUIMananger.m for more details.

When you use getInnerViewNode you can get the frame of ScrollView's inner view's frame. If you want to get the ScrollView's frame, you should use React.findNodeHandle(this._scrollView), and ScrollView's frame is not always equals to its inner view's frame.


(updated)

If you want to replace (...data)=>{console.log(data)} with callback, you should use it like this:

RCTUIManager.measure(this._scrollView.getInnerViewNode(), callback.bind(this));

Yo can use scrollToEnd() method of the ScrollView:

https://facebook.github.io/react-native/docs/scrollview.html#scrolltoend

According to the documentation: If this is a vertical ScrollView scrolls to the bottom. If this is a horizontal ScrollView scrolls to the right.

Tags:

React Native