React Native Expo VirtualizedList how to use scrollToEnd method? - reactjs

I am trying to use a scrollToEnd() method on VirtualizedList. But interestingly I cannot find any example on the internet explaining how to use methods with VirtualizedList.
I tried to use it like below, but obviously it didn't work
<VirtualizedList
data={serverData}
initialNumToRender={4}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
getItemCount={getItemCount}
getItem={getItem}
ListFooterComponent={renderFooter}
scrollToEnd={{ animated: true }}
/>

The scrollToEnd function is inherited from ScrollView by VirtualizedList. It is a function, not a prop.
You need to create a reference to the list component and call the function using this reference. This can be done as follows.
const listRef = useRef(null);
<VirtualizedList
ref= {listRef}
onContentSizeChange= {()=> listRef.current.scrollToEnd()}
/>
The above code scrolls to the end of the list when the content size of its data changes. However, you can call the scrollToEnd function whenever you want to.

VirtualizedListexpose scrollToEnd as method not props. You have to use React Ref to access it.
const listRef = React.useRef(null)
// Scroll to list end - Call this function after add new items to list for example
const scrollToListEnd = ()=> listRef.current?.scrollToEnd({ animated: true })
<VirtualizedList
data={serverData}
initialNumToRender={4}
renderItem={renderItem}
keyExtractor={(item) => item.id.toString()}
getItemCount={getItemCount}
getItem={getItem}
ListFooterComponent={renderFooter}
ref={listRef}
/>;

Related

react native: display props information that coming from another page

I'm trying to print EquipmentID inside the flatlist
And for some reason it does not show me its information.
I would like to understand what is wrong with my code.
The data (props) come from another screen .
in my example i show the flatlist screen and the props as it print.
const EquipmentContentCardOptions = props => {
const renderItem = ({ item }) => {
return <Text>{props.EquipmentID}</Text>;
};
return (
<FlatList
data={props}
renderItem={renderItem}
keyExtractor={item => item.EquipmentID}
/>
);
};
export default EquipmentContentCardOptions;
this is the props :
{
"EquipSetData":[
{
"EquipmentID":"567",
"EquipmentName":"aaa",
},
{
"EquipmentID":"123",
"EquipmentName":"rrr",
}
]
}
you are passing props directly to the FlatList and then trying to access the EquipSetData's data so what you need to do is
<FlatList
data={props.EquipSetData}
renderItem={renderItem}
keyExtractor={item => item.EquipmentID}
/>
Hope this would resolve your issue
I would suggest you console.log(props) as well as console.log(item) to see if they aren't null.If renderItem is meant to be a component, then, you have defined it inside another component which is wrong. Plus the name must start with a capital letter (if it is meant to be a component). You probably need to include the FlatList component to see how the props is passed to it.

React scroll to element (ref current is null) problem

I have a problem I'm not able to solve. The app got a component where a do looping array and making multiple elements off it. Then I want to make buttons in another component that will scroll to a specific element. (something similar to liveuamap.com when you click on a circle).
I tried the below solution, but got "Uncaught TypeError: props.refs is undefined". I could not find any solution to fix it.
The second question: is there a better or different solution to make scrolling work?
In app component I creating refs and function for scrolling:
const refs = DUMMY_DATA.reduce((acc, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
const handleClick = (id) => {
console.log(refs);
refs[id].current.scrollIntoView({
behavior: "smooth",
block: "start",
});
};
The refs I send to the article component as a prop where I render elements with generated refs from the app component.
{props.data.map((article) => (
<ContentArticlesCard
key={article.id}
ref={props.refs[article.id]}
data={article}
onActiveArticle={props.onActiveArticle}
activeArticle={props.activeArticle}
/>
))}
The function is sent to another component as a prop where I create buttons from the same data with added function to scroll to a specific item in the article component.
{props.data.map((marker) => (
<Marker
position={[marker.location.lat, marker.location.lng]}
icon={
props.activeArticle === marker.id ? iconCircleActive : iconCircle
}
key={marker.id}
eventHandlers={{
click: () => {
props.onActiveArticle(marker.id);
// props.handleClick(marker.id);
},
}}
></Marker>
))}
Thanks for the answers.
Ok so i found the solution in library react-scroll with easy scroll implementation.

How can I render Alarm item in FlatList with typescript?

I'm making a FlatList rendering Alarm items. (react-native-simple-alarm)
the tsx file where my FlatList is
const [alarms, setAlarms] = useState<AlarmType[]>([]);
...
const renderItem: ListRenderItem<AlarmType> = ({item}) => {
return <ListItem props={item} />; // i have an error here on props
};
...
<FlatList
ref={flatListRef}
scrollEnabled={scrollEnabled}
data={alarms}
renderItem={renderItem}
keyExtractor={item => item.oid} //error here on keyExtractor
/>
the Alarm list items that FlatList renders
import {Alarm as AlarmType} from 'react-native-simple-alarm/dist/Types';
const ListItem = (props: AlarmType) => {
const deleteItem = async (oid: string | number) => {
try {
await deleteAlarmById(oid);
...
in ListItem, I also get errors saying that oid cannot be undefined.
Not sure it's because of this problem, but the time properties of alarm instances that i made in application get messed up, as I make another instances
How can I use Alarm type perfectly?? please help me
You are passing the props incorrectly.
When you do something like
<ListItem props={item} />
You are passing props that contain a property called props, so in ListItem, the prop would be something like {props: item}.
But you want the prop in ListItem to be the item object, so you need to do something like
<ListItem {...item} />
To understand better, you can take a look at the docs for ... (triple dots) and also read Components and Props.

Can rxjs control the maximum frequency of a function call?

I am making a react-native app. Since the onEndReached props in FlatList is problematic, onEndReached can be triggered more than once when the end is reached.
I have heard of rxjs that can make a button's onPress only be triggered once with some condition even user is clicking on it multiple times.
Below is the Flatlist:
<FlatList
data={paginatedList}
ListHeaderComponent={() => this.renderHeader()}
renderItem={({item, index}) => this.renderItem(item, index)}
onEndReachedThreshold={0}
onEndReached={(distanceFromEnd) => {
console.log(distanceFromEnd);
this.setState({normalListLength: normalListLength + 10})
}}
/>
I want the this.setState function to be limited to once per seconds (1000ms) . Is that I should use rxjs to do this?
So one possible solution could be to have a Subject which you can next() the new value (distanceFromEnd) into. And then you can apply any combination of operators (including debounceTime) to enforce the frequency limiting.
Keep in mind my React syntax might not be spot on
<FlatList
data={paginatedList}
ListHeaderComponent={() => this.renderHeader()}
renderItem={({item, index}) => this.renderItem(item, index)}
onEndReachedThreshold={0}
onEndReached={(distanceFromEnd) => {
console.log(distanceFromEnd);
myOnEndReachedSubject.next(distanceFromEnd);
this.setState({normalListLength: normalListLength + 10})
}}
/>
// elsewhere define subject
myOnEndReachedSubject = new Subject<number>();
// ....elsewhere in a lifecycle function
componentDidMount() {
myOnEndReachedSubject
.debounceTime(1000) // debounce for a second
.distinctUntilChanged()
.subscribe((distance) => {
// Do something with distance
// setState etc
});
}

VirtualizedList: You have a large list that is slow to update

I use FlatList with large number of items. I get following alert from Expo XDE.
VirtualizedList: You have a large list that is slow to update - make
sure your renderItem function renders components that follow React
performance best practices like PureComponent, shouldComponentUpdate,
etc. {"dt":13861,"prevDt":1498372326027,"contentLength":6624}
I used some optimization approaches to my FlatList for example PureComponent, but I still get this alert. Before I will describe my optimizations, could you tell me if this alert appears always even though FlatList is optimized? Or maybe it indicated actual issues with performance? I ask because performance of my FlatList is good.
I was previously seeing this error. After optimizing my code, I no longer see it. I figured out the problem by adding console.log() statement to the render() function of the Component that creates the FlatList, and the function that renders each item in the List. I noticed that my code was previously re-rendering the entire FlatList and all its items whenever there's a state change to any component on that page (even a component that's not related to the FlatList). I fixed this by converting various components to PureComponents. Here's what my FlatList declaration looks like:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
data={allPosts}
initialNumToRender={7}
renderItem={({ item }) =>
<Post postJson={item} isGroupAdmin={isGroupAdmin} user={user} />
}
/>
Notice that I'm returning <Post /> which is a pure component:
import React, { PureComponent } from 'react';
class Post extends PureComponent {
render() { ... }
}
This ensures that the FlatList re-renders a only if the post changes. When I was previously passing a normal function to renderItem i.e., a function that does something like this:
return (
<View>
...
</View>
);
I noticed that the FlatList was re-rendering all items whenever any item changed. Now, by using a PureComponent, the FlatList only renders the new item added to the list (if the list is already being displayed).
It still takes relative long to render the entire list the first time. However, initialNumToRender ensures that the screen is filled up pretty much instantaneously (while the remain items get rendered in the background). And more importantly, after that initial rendering, the FlatList only ever has to render one item at a time (the item that changes).
I found this post very helpful).
I've just realized this is also explained here
I noticed that the answer to this question dosen't proffer solution for those using functional component and hooks. I encountered this problem and i was able to get rid of it by using the hook "useMemo()"
<FlatList
keyExtractor={keyExtractor}
data={productsState.products}
renderItem={renderItem}
/>
const renderItem = ({ item }) => (
<ListItem
title={item.ProductName}
subtitle={(item.ProductQuantity) + " " + (item.QuantityType !==
null ? item.QuantityType : " ") }
bottomDivider
topDivider
chevron
checkmark={checkMark}
onLongPress={() => setCheckMark(!checkMark)}
rightSubtitle={(item.Currency !== null ? item.Currency: " " ) +
" " + (item.productCost !== null ? item.productCost: " " )}
rightSubtitleStyle={{ marginTop: -20 }}
badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
/>
)
The renderItem function is an expensive computation, because it a long list to render. Instead I memoize it as follows
const memoizedValue = useMemo(() => renderItem, [productsState.product]);
<FlatList
keyExtractor={keyExtractor}
data={productsState.products}
renderItem={memoizedValue}
/>
const renderItem = ({ item }) => (
<ListItem
title={item.ProductName}
subtitle={(item.ProductQuantity) + " " + (item.QuantityType !==
null ? item.QuantityType : " ") }
bottomDivider
topDivider
chevron
checkmark={checkMark}
onLongPress={() => setCheckMark(!checkMark)}
rightSubtitle={(item.Currency !== null ? item.Currency: " " ) +
" " + (item.productCost !== null ? item.productCost: " " )}
rightSubtitleStyle={{ marginTop: -20 }}
badge={{ value: item.sellingPrice, textStyle: { color: 'orange' }, containerStyle: { marginTop: -20 } }}
/>
)
Don't forget to import useMemo from react, inorder to make this work.
Good Luck!
If you are using a functional component, wrapping the component in memo is a good way to prevent unnecessary renders without going through the hassle of converting a functional component to a pure class component. This post explains it more
follow this example:
In the parent component:
import React from 'react';
import {FlatList} from 'react-native';
import PostCard from './PostCard';
export const NewsFeeds = props => {
return (
<FlatList
data={data}
initialNumToRender={4}
refreshing={loading}
renderItem={_renderitem}
/>
);
};
const _renderitem = ({item}) => <PostCard item={item} />;
In the child component
import React, {memo} from 'react';
import {View} from 'react-native';
const PostCard = (props) => {
return (
<View>
</View>
);
};
export default memo(PostCard);
If you are using a class component, make sure your component is a pure component by extending React. PureComponent in your class definition
class NewsFeeds extends React.PureComponent {
render() {
return (
<FlatList
data={data}
initialNumToRender={4}
refreshing={loading}
renderItem={_renderitem}
/>
)
}
}
Adding this prop :
initialNumToRender={n}
worked for me (n being a considerably short amount, for example 5).
I figured it out, why this bug is happened. The main problem is, when your onEndReached event is happened, im sure you are loading something from server, which means, you need to wait until your loading is finished from server, so after that you can call onEndReached event.
But in your case there is multilple calling of onEndReached event. So when it happens, your application was trying to load datas from server again and again.
Ok, how to solve this problem: you need to create new state, for example
this is realization of infinite scrolling by pagination.
const [loader, setLoader] = useState<boolean>(false);
const onEndReached = (page) => {
if (next && !loader) {
setPage(page + 1)
}
}
const loadData = async () => {
setLoader(true);
const resp = await getData();
setLoader(false);
}
<FlatList ...someprops onEndReached={onEndReached} />
On top of all the answers given, you can also try setting removeClippedSubviews to true.
<FlatList
removeClippedSubviews
// ...other props
/>
By enabling removeClippedSubviews the memory is freed up when an item disappears from the view. When you have a long and complex list (i.e. a list of cards) the DOM of each card can get pretty large so it's best to free up the memory when it's not visible.
In addition if you combine with useCallback() rather than useMemo() you free up a bit more memory when your "data" changes
const renderItem = useCallback(originalRenderItem, [data])
the useMemo() approach will memoize based on the value, but it should really free itself up when the data changes. By doing useCallback() you're getting the benefit of using the "function as a parameter" so you don't need to
const renderItem = useCallback(({item, index}
=> originalRenderItem({item, index}), [data])
Thus making it look like a wrapped function without as much reading for the next person.
Doing this two:
prevents the calling the potentially expensive render() function of recently updated components.
reduces memory used by invisible components
frees up the memoized data if data changes sooner.
Also make sure, you don't encapsulate FlatList with ScrollList.
For me it accidentally appears, because I used native-base, and didn't noticed, that their Component <Content> replace ScrollList.
For more Information see here: https://stackoverflow.com/a/54512633/1256697
add memo to your renderItem component when export it
import React,{memo} from "react";
.
.
.
your code
.
.
.
export default memo(your component name);

Resources