Programmatically set scroll index of react native VirtualizedList - react-native

I have a book reader application, I implement book content thorough react native Virtuallist. In some case it needs to scroll to some titles in the book that can be out of screen and not rendered yet. If I load whole book's content I faced some performance issue. I need a way to change Virtuallist index or shift it on paragraph selection. this is what I implemented so far but it scroll to bottom constantly to render paragraph I chose, And looks so ugly.
const getItem = (_, index, focusedRank) => {
return data[index];
};
return (
<VirtualizedList
ref={(refs) => (flatListRef.current = refs)}
data={DATA}
initialNumToRender={20}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemCount={() => data.length}
getItem={(_, index) => getItem(_, index, focusedRank)}
contentContainerStyle={{padding: 10}}
onScrollToIndexFailed={(info) => {
const wait = new Promise((resolve) => setTimeout(resolve, 500));
wait.then(async () => {
flatListRef.current.scrollToOffset({
offset: info.averageItemLength * info.index,
animated: true,
});
await flatListRef.current?.scrollToIndex({
index: focusedRank,
animated: true,
});
});
}}
/>
)
If I could change index or shift by focusedRank the problem's solved.

Why don't you use Flatlist. And also Flatlist has onLayout you could implement it using regression algorithm or like that. By implementing it you have a simple time to navigate between paragraph anymore

Related

How do I get the current item displayed in a FlatList?

I'm using a flatlist to render a feed of videos for my project. I want to prevent the videos from auto-playing when they're not visible on the screen.
I figured the simplest way to do this would be to see if the current video on the screen is currently visible/active and if so, I can set props to true for playing etc.
I'm having a lot of issues trying to achieve this however an I wanted to know if anyone can give me some pointers as I'm new to React-Native.
You can use onViewableItemsChanged prop from FlatList
Docs Here
Working Example of FlatList of Videos with automatic play/pause feature
Something as shown below
const [Viewable, SetViewable] = React.useState([]);
const ref = React.useRef(null);
const onViewRef = React.useRef((viewableItems) => {
let Check = [];
for (var i = 0; i < viewableItems.viewableItems.length; i++) {
Check.push(viewableItems.viewableItems[i].item);
}
SetViewable(Check);
});
const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 80 });
<FlatList
data={Videos}
keyExtractor={(item) => item._id.toString()}
renderItem={({ item }) => <VideoPlayer {...item} viewable={Viewable} />}
ref={ref}
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
/>

how can we autoscroll and autoloop flatlist in react native

I want my Flatlist autoscroll and loop in horizontal direction, please help me out
Thanks in Advance
<FlatList
style={{marginEnd:5}}
timer={2000}
// width={deviceWidth}
loop={true}
// keyExtractor={this._keyExtractor.bind(this)}
horizontal={true}
onScrollToIndexFailed={info => {
const wait = new Promise(resolve => setTimeout(resolve, 500));
wait.then(() => {
flatListFeaturedMobiles.current?.scrollToIndex({ index: info.index, animated: true });
});
}}
// onScrollToIndexFailed={()=>{}}
flatListRef={React.createRef()}
ref={this.flatListFeaturedMobiles}
autoscroll={true}
showsHorizontalScrollIndicator={false}
data={this.state.featuredMobiles}
horizontal
You can scroll the list using Animated like check it How do I make a list (FlatList) automatically scroll
You can also use react-native-autoscroll-flatlist#readme
For more detail you can read here wants-to-autoscroll-flatlist-in-react-native

How to use scrollToLocation in sectionList to scroll to specific location

I have a SectionList as follows,
<SectionList
ref={ref => this.sectionListRef = ref}
renderItem={({ item, index, section }) => this.renderItems(item, index, section)}
renderSectionHeader={({section: {title}}) => this.renderSectionHeader(title)}
stickySectionHeadersEnabled={false}
sections={openRequestsArrLength > 0 ? openRequestsArr : []}
onScroll={(e) => this.handleScroll(e)}
/>
and below is the code to take the user to desired list item
this.sectionListRef.scrollToLocation({
viewOffset: scrollPosition,
animated: false,
itemIndex,
sectionIndex
});
when this code executes getting following error,
scrollToIndex should be used in conjunction with getItemLayout or
onScrollToIndexFailed
I am not sure about how to user neither getItemLayout nor onScrollToIndexFailed.
Can anybody let me know how can i proceed form this point to achieve my requirement that scrolling to desired location?
Thank You.
Edit: getItemLayout didn't solve my problem, it just prevent the error that stated above. Any suggestions on how to use onScrollToIndexFailed to make scrollToLocation works

Using a paged FlatList, how to get an event on visible page changed?

I am building a simple horizontal React Native FlatList with paging enabled:
<FlatList
horizontal={true}
pagingEnabled={true}
data={...some data...}
renderItem={({item}) => {...some rendering...}}
onViewableItemsChanged={(info) => {... handling ...}}
/>
I would like to get called back only after a new page in the list has been made visible. The behavior that I am seeking is that, as the user swipes the list left and right, the list will page through the items and I want the callback to fire once with the visible items.
The onViewableItemsChanged props is called on every change in visible items which is not what I am looking for, unless I keep track on which page the items belong to.
I am looking for a onViewablePageChanged type of callback.
Any ideas on how to achieve this?
The solution I used was to track the viewable items against the overall list of items to determine the "page" number of viewable items.
By using pagingEnabled, the list will only show pages.
However onViewableItemsChanged is called back on every changes to the viewable items. By comparing the viewable items againts the list of items, one can figure out on which "page" the FlatList is.
This requires that one knows how many items can be displayed in the view. Either calculated based on layout or set in the View and FlatList rendering.
Here's an example:
onViewableItemsChanged = ({viewableItems}) => {
// Get the first viewable item
const firstViewableItem = viewableItems[0].key;
// Get its index into the items
const index = this.state.items.findIndex(item => item.key === firstViewableItem);
// If the index is a multiple of the number of items displayable on the screen
// by checking for a reminder on the modulo operation
if ((index % NB_ITEMS_SCREEN) === 0) {
// get page
const currentPage = index / NB_ITEMS_SCREEN;
if (currentPage !== this.state.currentPage) {
// Do something and update currentPage in this.state
}
}
}
<FlatList
data={this.state.items}
horizontal={true}
pagingEnabled={true}
renderItem={({ item }) => <Text>{item.key}</Text>}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
See this snack
https://snack.expo.io/#visto9259/flatlist-onviewableitemschanged-example
You can achieve visible items by using below code. Try this.
//Method to invoke when item change
onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems);
console.log("Changed in this iteration", changed);
}
render () => {
return (
...
<FlatList
horizontal={true}
pagingEnabled={true}
data={...some data...}
renderItem={({item}) => {...some rendering...}}
onViewableItemsChanged={this.onViewableItemsChanged }
viewabilityConfig={{
itemVisiblePercentThreshold: 50
}}
/>
....
)}

Prevent Flatlist from scrolling when new items are added

When adding data to the Flatlist (e.g. subscriptions) it scrolls down leading to a very bad UX.
Any idea on how this could be solved?
Actually, I think this must be handled in native level, but not handled yet,
I solve my problem by saving scroll offset and set it again after reloading data like this:
reloadData(flatListData){
this.setState({
flatListData: flatListData
});
requestAnimationFrame(() => {
this.flatList.scrollToOffset({
animated: false,
offset: this.flatListLastOffset
});
});
}
...
<FlatList
data={this.state.flatListData}
ref={ref => this.flatList = ref}
onScroll={(event: Object) => {
this.flatListLastOffset = event.nativeEvent.contentOffset.y;
}}
horizontal={false}
scrollEventThrottle={16}
/>
this is not the best solution, but can fix my problem for now