ListView fails to scroll - react-native

As you can see in the attached GIF, I can't scroll through the ListView. The view snaps back to the original position as soon as I release the mouse.
I've read over the documentation and it seems like scrolling should just work. Here's the implementation of render():
render: function(){
return (
<ListView
automaticallyAdjustContentInsets={false}
initialListSize={9}
dataSource={this.state.dataSource}
renderRow={this.renderTrack}
renderSectionHeader={this.renderSectionHeader}/>
);

Your ListView might actually be quite small but not appear that way do to visible overflow. Try setting flex: 1 or height: 587 on your ListView's style property.

The only time I've seen this is when contentContainerStyle is set to flex:1. Remove the flex if that's the case.

setting flex:1 to list's parent did the trick for me.

I had the same problem, I had a View wrapping the ListView, I removed the outer View and now it works as expected.

It is due to this property, I assume you only have one, maybe add a sticky header per row?:
renderSectionHeader function
(sectionData, sectionID) => renderable
If provided, a sticky header is rendered for this section. The sticky
behavior means that it will scroll with the content at the top of the
section until it reaches the top of the screen, at which point it will
stick to the top until it is pushed off the screen by the next section
header.

Related

How to stay in position when new messages are loaded in a ScrollView in React-Native?

I have a ScrollView containing messages (most recent messages are further) in a chat application.
I limit the number of pre-loaded messages, and I dynamically load a new batch when the Scroll View is scrolled to the top. So when new messages are loaded, the current scrollPos is at 0.
The problem is that when the new messages arrive, the scrollPos stays at 0, so the user is teleported to the oldest newly loaded message.
I have tried to deal with it by manually scrolling back down to the position using the size of the content change, but this is not satisfying as the user sees a back and forth scrolling.
Can someone think of a way to do this so that the user does not see any change when the new messages appear an can simply gradually scroll up to see them.
I found a way to do it.
The idea comes from the Invertible Scroll View component: https://github.com/expo/react-native-invertible-scroll-view
I didn't use the component but implemented the idea directly on the Scroll View to have minimal changes in my code.
To explain, we translate vertically the Scroll View using the style of the Scroll View and transform: [{ scaleY: -1 }]. We do the same for the children. Then, we revert the order of the messages.
In that setup, the scrollPos() measures from the visual bottom. To trigger the loading of the new messagges, now I use
const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 20;
return layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom;
};
The trick is that now, when the new messages appear on top, you have nothing to do as the distance from the user's point of view to the bottom does not change.
You can use the onContentSizeChange prop to scroll to the bottom anytime it detects a change in content size.
The scrollToEnd function may differ depending on RN version.
<ScrollView
...
onContentSizeChange={() => this.scrollView.scrollToEnd({animated: true})}>
</ScrollView>
Ref: https://reactnative.dev/docs/scrollview#scrolltoend

Why aren't all items displayed in the ScrollView?

I have a Flatlist inside a ScrollView with 100 items, but no matter what height I give to the ScrollView or Flatlist, there are always just 18 items displayed on my Phone. After that the slider of the ScrollView is still scrolling down, but the list ends after 18 items. I want to be able to scroll through the complete list of items with the ScrollView containing the FlatList. I am using a Samsung Galaxy S8+ as a test device.
Here is a snack of the problem: https://snack.expo.io/#christophhummler/stickyheaderscrollscreens
Thanks for your help :)
I think it has to do with the scrollEnabled={false}, set it to true or remove the prop altogether otherwise it may be your todos array
Edit
Since you want to create a sticky header you must set the height to be the device height of screen + height of header, change styles.scrollView to :
scrollView: {
height: height + 200,
}
also on android there is an issue of nesting scrollviews, to enable a nested scrollview add the nestedScrollEnabled prop:
<FlatList
...
nestedScrollEnabled={true}
/>
You will have to implement logic to enable the Flatlist scroll view when the specific threshholds are met. It works at the moment in conjunction with scrollEnabled={true}
i hope this helps

Hold Position of ScrollView when adding data

I am trying to make it so whenever something new is rendered into a scroll view, the scroll view will stay put and not bump up and down. Right now if a new component is rendered in, the scrollview appears to be reset to 0.
Is there a way to stop this behavior, and hold position?
Right now for the scrollview I am using:
handleScrollt(event){
this.scroll = event.nativeEvent.contentOffset.y
}
handleSizet(width, height){
if (this.scroll) {
const position = this.scroll + height - this.height
this.refs.sv.scrollTo({x: 0, y: position, animated: false})
}
this.height = height
}
<ScrollView
ref="sv"
scrollEventThrottle={16}
onScroll={this.handleScrollt.bind(this)}
onContentSizeChange={this.handleSizet.bind(this)}
The issue with this is the scrollview will render briefly, before then scrolling to the correct offset. So it seems like theres a brief splash of the top of the screen
you have to define a height for the scrollview.
If the scrollview is supposed to cover an entire View you can get the view's height by:
<View style={styles.contactView} onLayout={(event) => {
var contactViewY = event.nativeEvent.layout.y;
this.setState({contactViewY: contactViewY})
}}>
and then give it to the scrollview
<ScrollView style={[styles.contactScroller, {height: this.state.contactViewY}]}>
Bear in mind that the onLayout method is called immediately once the layout has been calculated, so if the view's height changes later, this two lines of code alone won't update it.
You should try maintainVisibleContentPosition.
Example:
<ScrollView
{...props}
maintainVisibleContentPosition={{
minIndexForVisible: 0,
}}
/>
From the docs:
maintainVisibleContentPosition
When set, the scroll view will adjust the scroll position so that the
first child that is currently visible and at or beyond
minIndexForVisible will not change position. This is useful for lists
that are loading content in both directions, e.g. a chat thread, where
new messages coming in might otherwise cause the scroll position to
jump. A value of 0 is common, but other values such as 1 can be used
to skip loading spinners or other content that should not maintain
position.

Scrollview and child with flex: 1

Is it possible to have a layout which is wrapped with ScrollView:
<ScrollView>
<View>dynamic height</View>
<View>flex with minHeight</View>
<View>static height</View>
</ScrollView>
and meets below prerequisites:
Height of the first View is dynamic (depends on text length). Height of the third View is static (always three buttons inside). Rest of the screen should be filled with View with map, but the minimal height is set to 250.
Now the tricky part: if there is a lot of text in first View, so that map doesn't fit, a scroll should appear. I couldn't achieve that. I was trying to give the map View flex: 1 and minHeight: 250, but it's not rendered at all.
Solution
Ok, I've found a way to fix it. In first render, I get screen height (Dimensions) and height of first and third view (onLayout). Then, I calculate the height of second view (screenHeight - view1 - view3 - naviagtionHeight) and forceUpdate() to re-render it.
Add contentContainerStyle={{flexGrow: 1}} prop to ScrollView and its children's flex:1 would work. So you don't need to calculate it manually.

Setting an initial scroll position of a React Native ListView

I'm trying to set the initial scroll position of a ListView in react native.
Right now, I'm doing this:
componentDidMount() {
let i = this.props.images.indexOf(this.props.current);
if(i != -1) {
this.refs.listView.scrollTo({ x:i*this.props.width, y:0, animated:false });
}
}
where the images prop is the datasource for the ListView, the current prop is where I want to be initially scrolled to, and the width prop is the width of the component. (The component scrolls horizontally).
This works, but there's a delay between the initial render and the call to componentDidMount, giving me a flash of the end of end of the list before the list is scrolled.
Is there a way of setting an initial scroll position of the list? Or better way of doing this to get rid of that flash?
On iOS you can use the ScrollView#contentOffset to set the initial scroll position of a ListView, which inherits the properties of ScrollView.
If you are looking for an Android solution, the ListView.scrollTo method you are calling seems like the best bet for the general case.
That said, if I interpret your code sample correctly, you are using the ListView for a paginated, horizontal list, perhaps with the help of the pagingEnabled property? If this is the case, on Android you might look at using ViewPagerAndroid instead, and setting the initialPage property.