I have a list using the ScrollView of react native itself. Basically, I build a list dynamically through an API return.
async fetchData(userSearch) {
const {route} = this.props;
const {params} = route;
const {type} = params;
this.setState({
loading: true,
});
const responseProcedures = await scheduleResource.getProcedures(userSearch);
this.setState({
procedures: responseProcedures.data,
loading: false,
});
}
<ScrollView
onScroll={(event) => this.shouldLoadMoreContent(event)}
>
{procedures.map(procedure => (
<ArrowBox key={procedure.id} onPress={() => RootNavigation.navigate('ProcedureDetails', {procedure})}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
SinĂ´nimos
</Text>
<View style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map(synonym => <Text style={styles.ProcedureSynonymOption} key={synonym}>{synonym}</Text>)}
</View>
</>
)}
</ArrowBox>
))}
</ScrollView>
The problem is that I load the entire return from the API and it slows down.
I would like to know how to dynamically load the content and make new calls in the api, when I reach the end of the page.
Api allows me to place offset and limit.
Could someone give me some example?
Thanks!!!!!
Basically the ScrollView is not designed to handle dynamic data, the correct component that is designed to perform this function is called Flatlist. It works almost exactly like ScrollView but it is faster and will only render components that are shown on the screen.
Please import Flatlist from React Native like this...
//At the top of your file, please import FlatList together with all the modules that you want
import { FlatList, Text, View } from "react-native";
Then replace the entire ScrollView in your code with a Flatlist like this:
<FlatList
keyExtractor={(procedure) => procedure.id}
data={this.state.procedures}
renderItem={(procedure) => {
return (
<ArrowBox
key={procedure.id}
onPress={() =>
RootNavigation.navigate("ProcedureDetails", {
procedure })}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
SinĂ´nimos
</Text>
<View
style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map((synonym) => (
<Text
style={styles.ProcedureSynonymOption}
key={synonym}>
{synonym}
</Text>
))}
</View>
</>
)}
</ArrowBox>
);
}}
></FlatList>;
Related
I want like that flatlist render only 5 checkbox items and then when I click to +5 more button it will show 5 more checkbox list.In this all checkbox list appearing but i want only five
Please help me how to achieve that
Thanks in advance
const renderResourceList = renderData => {
return (
<FlatList
data={renderData}
initialNumToRender={5}
maxToRenderPerBatch={5}
pagingEnabled={true}
nestedScrollEnabled={true}
renderItem={({item}) => (
<View style={styles.card}>
<TouchableOpacity
onPress={() => {
if(resourceTypeArray.includes(item)){
setResourceTypeArray(currentList => {
return currentList.filter(items => items !== item);
});
}
else{
setResourceTypeArray(currentList => [
...currentList,
item
]);
}
onSetResourceType(item);
}}
style={styles.modalBtn}>
<Icon
name={
resourceTypeArray.includes(item) ? 'checkbox-marked' : 'checkbox-blank-outline'
}
size={18}
color="#353C3C"
style={{bottom: -1}}
/>
<View style={styles.textWrapper}>
<Text style={styles.modalText}>{item.charAt(0)}
{item.toLowerCase().slice(1).replace(/_/g, ' ')}</Text>
</View>
</TouchableOpacity>
</View>
)}
/>
);
};
I tried but this not working
I used a package called flatlist-react to handle this for me. A decent tutorial for this can be found here.
With this package, you can directly specify and limit the items rendered with the limit prop. Here is an example:
<FlatList
limit="2,-2"
list={people}
renderItem={Person}
/>
If you keep track of the limit prop variables using state, you can dynamically change these values when you click 5+ more in order to render whatever part of your data you would like.
Let's say I have a FlatList component that renders items whose counts can be anywhere between 10 to 100+.
The official doc on FlatList optimization says "Try to avoid a lot of logic and nesting in your list items." I want to understand better what this actually means.
For the item components, I have a couple of options (Examples are simplified, the actual components are much more complicated obviously):
Option 1: Using a nested structure, e.g. :
const ItemComponent = (props) => {
const ChildComponent1 = (props) => {
return (
<View>
<Image source={props.image}/>
</View>
)
}
const ChildComponent2 = () => {
return (
<View>
<Text>This is Child Component.</Text>
</View>
)
}
return (
<View>
<Text>This is {props.name} </Text>
<ChildComponent1 image={props.image}/>
<ChildComponent2/>
</View>
)
}
Option 2: Using a non-nested structure, e.g. :
const ItemComponent = (props) => {
return (
<View>
<Text>This is {props.name} </Text>
<View>
<Image source={props.image}/>
</View>
<View>
<Text>This is Child Component.</Text>
</View>
</View>
)
}
Am I actually better off in terms of performance if I use option 2?
If so, how much performance gain will I have and why? I'd like to understand deeper, but cannot find a resources that explains the rationale in detail.
Thanks!
Hi I'm working on a mobile application in react native. Its a simple app that lets you search for ingredients in a search bar.
I have a small bug which I can't seem to fix regarding the scrollview and setstate. I have a function that displays the search list under my search bar. It is an absolutely positioned element. The bug occurs when I try to scroll down on the scrollview and press on one of the items, instead of the scrollview remaining in place it scrolls directly to the top. Ideally I would like the scrollview to remain in place once the user has pressed on a item. I feel like it has something to do with how I'm calling setstate. The function is as follows:
const [userIngredients, setUserIngredients] = useState([]);
const OutputSearchList = () => {
const clickIngredient = (i) => {
if (!userIngredients.includes(i)) {
setUserIngredients((temp) => [...temp, i]);
} else {
const temp = userIngredients.filter((item) => item !== i);
setUserIngredients(temp);
}
};
return ingredients.map((item) => {
return (
<TouchableOpacity
style={styles.searchBarListItem}
onPress={() => clickIngredient(item)}
key={item}
>
<View style={styles.searchBarListContentChecked}>
<Text style={styles.searchBarListText}>{item}</Text>
<Ionicons
style={styles.searchBarListAdd}
name="checkmark-circle-outline"
size={28}
color="#000000"
/>
</View>
</TouchableOpacity>
);
});
};
and the function is called like so:
<ScrollView>
{ingredients?.length > 0 ? (
<View>
<OutputSearchList />
</View>
) : searchText?.length > 0 ? (
<View>
<Text>No ingredients found</Text>
</View>
) : (
<View>
<Text>Search for an ingredient (e.g. Eggs, Beef, Rice)</Text>
</View>
)}
</ScrollView>
I'm struggling to understand what causes my component to rerender as there is no state present here. Code to focus on is Flatlist as this is React Native but this is about React and not specifically RN. As you can see inside Flatlist I render renderPost. I know that renderPost, aka reusable component PostListItem rerenders because this console.log you can see inside flatlist gives me repeated keys, and even console logging postsSortedByDate shows me that they render few times. Because of this, I even have a warning child list keys must be unique while I definitely gave flatlist unique IDs from my array, that warning is because of these rerendering. I guess I need to use Memo somewhere?:
const FeedScreen: React.FC<FeedScreenProps> = ({ navigation }) => {
const { postsSortedByDate } = checkPostsContext();
const navigateToCreatePostScreen = async () => {
navigation.navigate("CreatePostScreen");
};
const renderPost: ListRenderItem<IPost> = ({ item }) => (
<PostListItem post={item} key={item.uniquePostID} />
);
return (
<ScreenContainer>
<SafeAreaView>
<Header
containerStyle={styles.containerStyleHeader}
headerTitle="Factory X Feed"
rightIcon="add-photo-alternate"
onPressRightIcon={navigateToCreatePostScreen}
/>
</SafeAreaView>
{postsSortedByDate.length === 0 ? (
<View style={styles.noPostsContainer}>
<Text>
No posts exist at the moment, please create one by tapping on the
image icon in the header. In the meantime, as indicated by the
spinner below, we will continue to try to fetch posts in case some
do exist but are just loading slowly.
</Text>
<ActivityIndicator size="large" color="#0000ff" />
</View>
) : (
<FlatList
data={postsSortedByDate}
renderItem={renderPost}
keyExtractor={(item, index) => {
console.log("IDS:", item.uniquePostID.toString());
//HERE IS THE ISSUE as here I see same keys rendering //multiple times, and I checked and I did not save posts in a wrong way //for few of them to have the same key somehow. Its now about that. //Even logging posts themselves(here) shows me they re render.
return item.uniquePostID.toString();
}}
/>
)}
</ScreenContainer>
);
};
My PostListItem component:
const PostListItem: React.FC<PostItemProps> = ({ post }) => {
return (
<View style={styles.container}>
{post.postImage ? (
<Lightbox
renderContent={() => {
return (
<Image
source={{ uri: post.postImage }}
resizeMode="cover"
style={[
styles.imageInLightbox,
{
width: width,
height: height,
},
]}
/>
);
}}
>
<Image
source={{ uri: post.postImage }}
resizeMode="contain"
style={styles.image}
/>
</Lightbox>
) : null}
<React.Fragment>
{post.postDescription ? (
<Text style={styles.textStyle}>
{hashtagFormatter(post.postDescription)}
</Text>
) : (
<Text style={styles.textStyle}>{defaultPostDescription}</Text>
)}
</React.Fragment>
</View>
);
So why is PostListItem, I mean renderPost rerendering and how to fix it?
I do list in my react native app.
I have this:
<FlatList
data={this.state.items}
renderItem={({ item }) => (
<ListItem
title={`${item.object.street_number}${item.object.apt_number?'/'+item.object.apt_number:''} ${item.object.street_name} ${item.object.city}`}
subtitle={`Payment: ${item.data[0].is_paid ? 'Paid' : 'No Paid'}`}
/>
)}
/>
Can i do the second subtitle or do line break?
It is totally up to you to define your own renderItem() function
renderItem({item}) {
const time = `${item.time}`;
const place = `${item.place}`;
const temp = css.addDegreesToEnd(item.currentTemp);
const {iconName, iconFont, iconColor} = item.icon;
let actualRowComponent =
<View style={css.home_screen_list.row}>
<View style={...}>
<Text style={...}>{time}</Text> //First text
<Text style={...}>{place}</Text> //Second
</View>
<Icon color={iconColor} size={...} name={iconName} //Others
type={iconFont}/>
<Text style={...}>{temp}</Text>
</View>;
...
}
Even complex layouts are also possible, not to mention two text rows, for instance: