React native flatlist scroll to top on data source change? - react-native

I have a flatlist with a dynamic data source. I change the redux state of the selectedCategory and use that to key into the data source object with the value being the data array for the flatlist.
When the data prop on the flatlist changes, the UI does update but when I try and scroll to the top with scrollToOffset when the data source changes it doesn't seem to work.
If I have scrolled down far then switch the data source to a smaller array it remains scrolled at the bottom.
<FlatList
ref={ref => (this.listRef = ref)}
style={styles.articleList}
refreshing={isFetching}
refreshControl={
<RefreshControl
refreshing={isFetching}
onRefresh={() => this.onRefresh()}
/>
}
onMomentumScrollEnd={x => this.onScroll(x)}
data={allNewsData[selectedCategory.id.toLowerCase()]}
renderItem={({item}) => (
<TouchableOpacity
style={styles.item}
activeOpacity={0.85}
title={item.post_title}>
<NewsArticleItem item={item} />
</TouchableOpacity>
)}
keyExtractor={item => item.ID.toString()}
/>
I tried to run on componentDidUpdate and the check is correct and runs when I change the data source for the flatlist, but I believe it is happening too fast? If I set a timeout of like 500ms around the scrollToOffset it does work but I'm not doing that...
componentDidUpdate(prevProps, prevState) {
if (prevProps.selectedCategory.id !== this.props.selectedCategory.id) {
console.log('data src changed');
this.listRef.scrollToOffset({animated: true, offset: 0});
}
}
It almost seems I need some type of callback to know when the data source of the flatlist changes maybe?

Related

Flatlist event when start reached

How we can implement onStartReached in FlatList ?
I used horizontal list.
I have some idea of using onScroll using,but I thing this not properly.
Seems onRefresh could do the job.
https://reactnative.dev/docs/flatlist#onrefresh
it must be start reached and pull to trigger onRefresh.
If you need a infinite scroll i used this
<FlatList
data={data}
renderItem={({ item, index }) => {
return (
// Component to render
);
}}
keyExtractor={(item) => item.uid}
onEndReached={() => //Load more data}
onEndReachedThreshold={0.5} // "Sleep" time
initialNumToRender={10} // initial number to items
/>
otherwise the way to use onStartReached is identical to onEndReached

Array not updating when doing setState()

I am implementing search functionality in my app. For displaying data I'm using FlatList. I tried all the methods on the web, but still I am unable to refresh my list based on search word.
<AppTextInput
placeholder="Search"
onChangeText={text => {
var backUp = [...this.state.originalData];
var temp = backUp.filter(item => {
if (item.toLowerCase().match(text.toLowerCase())) {
return item;
}
});
this.setState({
originalData: temp
});
}}
/>
<FlatList
data={this.state.originalData}
extraData={this.state}
renderItem={item => (
<View key={item.item}>
<Text>{item.item}</Text>
</View>
)}
keyExtractor={(item, index) => item}
/>
Please let me know how can I update the array in setState().
I think the approach needs to be updated.
First, if you keep filtering original data in the states and updates it, you won't get correct result.
And do not use this.state as a extraData props for FlatList.
Instead save input keyword to the state and set that value as a extraData props for FlatList

Prevent FlatList scrolling when new items are added

I have inverted vertical FlatList in my chat app, which shows newest message in bottom and oldest message in top (likes all other chat applications)
The problem is when I want to add new messages to bottom of my list, FlatList automatically jump to 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 newest messages to list
const data = [ ...newMessages, ...this.state.data ];
this.setState({ data });
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
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})
});
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.

How to make a Chat list like Facebook Messenger in React Native

I'm making a Chat app in React Native, in my app I have a Flatlist to show a message. When user pull the list, it will get new data and add to list. But the list will be re-render and scroll to start item. Is there anyway to make it still get data but stay in current position, like Facebook Messenger ?
I'm using FlatList like this :
<FlatList
refreshControl={
<RefreshControl
refreshing = {this.state.refreshing}
onRefresh = {this.addMessageToList}
/>
}
keyExtractor={(item, index) => index.toString()}
ref="flatList"
style={{ flex: 1 }}
data={newList}
renderItem={({ item, index }) => {
return (
<ChatContentItems item={item} index={index} parentFlatList={this}>
</ChatContentItems>
);
}}>
</FlatList>
Use onEndReached event to handle when user reached the end.
<FlatList
data={this.state.latestData}
keyExtractor={item => item.id.toString() }
renderItem={({item}) => <JobsListCell item={item}/>}
onEndReached={this.loadMore.bind(this)} // add this
onEndReachedThreshold={0.3} /> // and this. change according to your preference.
Since you have the full array with you, slice and append the next items to the latest array.
loadMore() {
let { latestData, fetchedData, incrementingAmount } = this.state;
if (latestData.length < fetchedData.length) {
this.setState({
latestData: [...latestData, ...fetchedData.slice(latestData.length, latestData.length + incrementingAmount)]
})
}
}
You can use scrollToEnd or scrollToItem after your addMessageToList completes
Also, ref strings are deprecated. You can use
ref={ (flatList) => this.flatList = flatList }

React-Native re-render Flatlist

I'm having trouble keeping the data in my Flatlist after coming back from another page. My scenario is as follows:
User goes to homepage and scrolls through 20 items
User clicks their profile tab changing page using react-native-router-flux
User clicks the home tab taking them back to the list however the list re-renders and starts from the top.
How can I stop this re-rendering and fetching the same data again?
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteReuest gets my data from firebase in batches of 5 and sets data: []
data: [...this.state.data, ...results]
I've tried using the below but not sure if this is correct, when i navigate away and back the data re-renders. I want to keep the data so the page will be exactly the same as when it was left.
shouldComponentUpdate(nextProps, nextState) {
if (JSON.stringify(this.state.data) !== JSON.stringify(nextState.data)) {
return true;
}
return false;
}
My flatlist:
<View>
<FlatList
scrollsToTop={false}
ref={(ref) => { this.flatListRef = ref; }}
showsHorizontalScrollIndicator={false}
onScroll={this.handleScroll}
data={this.state.data}
keyExtractor={item => item.key}
ListFooterComponent={this.renderFooter()}
onRefresh={this.handleRefresh}
refreshing={this.state.newRefresh}
onEndReached={this.handleEndRefresh}
onEndReachedThreshold={0.05}
getItemLayout={this.getItemLayout}
renderItem={this.renderItem}
/>
{this.state.refreshAvailable ? this.renderRefreshButton() : null}
</View>
Thanks for any help!
Coded long back for the dumb project, maybe this can help you
The View: used onLayout Prop for getting the y-axis
<ScrollView
ref={(ref) => this.scrollTo = ref}
contentContainerStyle={{margin:5,}}
>
<Card onLayout={(event) => this._findHeight(event.nativeEvent.layout, 'personal')}>
<Personal review={true}/>
</Card>
</ScrollView>
The Function: stored the y-axis; here i have used realm db
_findHeight = (e, name) => {
const {x, y, width, height} = e;
this.realm.write(() => {
this.realm.create('yLocation',{names:name,yaxis:y}) :
});
}
The AutoScroll Method: here i have used scrollTo method from ScrollView you can use any method using their ref
_scrollTo = (y) => {
this.scrollTo.scrollTo({x:0,y:y,animated:true});
}
Note : Call _scrollTo method in componentDidMount