Reputation: 2684
I have an inverted vertical FlatList
in my chat app, which shows the newest message at the bottom and the oldest message at the top (like all other chat applications)
The problem is when I want to add new messages to the bottom of my list, FlatList
automatically jumps to the bottom end of the list!
All I need is to prevent scrolling in this situation
Here is my FlatList
:
<FlatList
inverted
style={{flex: 1}}
data={this.state.data}
keyExtractor={(item, index) => item.id}
renderItem={this.renderItem}
/>
And here is the code to add the newest messages to the list
const data = [ ...newMessages, ...this.state.data ];
this.setState({ data });
Upvotes: 21
Views: 7149
Reputation: 5942
you can control this in react-native using the prop https://reactnative.dev/docs/next/scrollview#maintainvisiblecontentposition
As you can see from this video in PR #35319, you can modify this behaviour and avoid ScrollView to change position.
You can check their iOS Example and Android examples to better understand the API.
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.
The optional autoscrollToTopThreshold can be used to make the content automatically scroll to the top after making the adjustment if the user was within the threshold of the top before the adjustment was made. This is also useful for chat-like applications where you want to see new messages scroll into place, but not if the user has scrolled up a ways and it would be disruptive to scroll a bunch.
Caveat 1: Reordering elements in the scrollview with this enabled will probably cause jumpiness and jank. It can be fixed, but there are currently no plans to do so. For now, don't re-order the content of any ScrollViews or Lists that use this feature.
Caveat 2: This uses contentOffset and frame.origin in native code to compute visibility. Occlusion, transforms, and other complexity won't be taken into account as to whether content is "visible" or not.
https://github.com/facebook/react-native/pull/34141#issuecomment-1180556939
https://github.com/facebook/react-native/pull/35049
https://github.com/facebook/react-native/pull/35319
Upvotes: 1
Reputation: 644
You can use onViewableItemsChanged and scrollToIndex together to keep the viewableItems as per previous state.
const flatListRef = React.useRef();
const startIndexRef = React.useRef(0);
const handleListChange = () => {
// ...otherlogic
if (haseAddedToStart)
flatListRef.current?.scrollToIndex({
index: startIndexRef.current + 1,
animated: false,
});
};
const onViewableItemsChanged = React.useCallback(({ viewableItems }) => {
startIndexRef.current = viewableItems[0].index;
}, []);
Extra props to be added to Flat List are ref and onViewableItemsChanged
<FlatList
ref={flatListRef}
onViewableItemsChanged={onViewableItemsChanged}
// ...otherProps
/>
onViewableItemsChanged should come with useCallback, as changes to list would cause error.
It will scroll smaller than item height if the first item is half visible.
A more accurate result should be achievable via onScrollEndDrag and onMomentumScrollEnd but it needs calculating items height.
Upvotes: 0
Reputation: 1
Put this in the view
<FlatList
data={this.state.data}
ref={ref => {
this.flatListRef = ref;
}}
initialScrollIndex={0}
keyExtractor={item => item.id}
extraData={this.state.refresh}
renderItem={({item, index}) => {
// animation={animation}
return (
<ListItem
inAnimation={inAnimation}
outAnimation={outAnimation}
duration={duration}
easing={easing}
isDeleted={item._isDeleted}
id={item.id}
item={item}
/>
);
}}
`/>`
Run this in a function
this.flatListRef.scrollToIndex({
animated: true,
index: nextProps.indexScroll})
});
Upvotes: 0
Reputation: 849
You should use a workaround to achieve this. If you check the documentation for FlatList (which extends the props of ScrollView), you'll see that all you need to do is set the scrollEnabled prop to false to disable scrolling. How and where you choose to do this will be up to you since you didn't really post a lot of code. A simple way to handle this would be to use state:
<FlatList
...
scrollEnabled={this.state.scrollEnabled}
/>
In your case you could change the state when the new data is being loaded and change it back when it is rendered.
There is an open issue on Github about this case.
Upvotes: -1
Reputation: 185
Your case look simple but you are adding new message at top then reverse it to bottom last position using inverted
flag
Could remove inverted
flag and add new item at last simply const data = [...this.state.data, ...newMessages];
<FlatList
style={{flex: 1}}
data={this.state.data}
keyExtractor={(item, index) => item.id}
renderItem={this.renderItem}
/>
const data = [...this.state.data, ...newMessages];
this.setState({ data });
I hope this will work
Upvotes: 0