Dealing With Multiple Flatlists on the same Screen - reactjs

Please help my deal with unnecessary re-renders when having two flatlists on the same screen
My screen requires two flatlists-
For HOSTS
For QUEUE
When the component mounts, I get data from the api call like this-
{
"hosts": [{"id":"1", "name":"kyouma"},...],
"queue": [{"id":"99", "name":"eren"},...]
}
Now what I do is I store my hosts and queue separately in my redux store like this-
this.props.dispatch({
type: GET_ROOM_HOSTS,
payload: info['hosts']
})
this.props.dispatch({
type: GET_ROOM_QUEUE,
payload: info['queue']
})
where info is the object received from the api call as shown above. Now I mapStateToProps these two from the redux store to the default screen component such that-
this.props.roomQueue is for queue and
this.props.roomHosts is for hosts
My FlatList's are like this-
<FlatList
data={this.props.roomQueue}
horizontal={true}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
<FlatList
data={this.props.roomHosts}
numColumns={3}
keyExtractor = {item => item.id}
renderItem({item} => {
return(
<customComponent (with suitable props) ..../>
)
})
/>
PLEASE NOTE that both the FlatList's are present in the same Component (React.Component) of the screen and are displayed at different parts of the screen(queue at bottom of the screen and hosts at the top of the screen). Also queue and hosts are independent of each other. Screen looks like this
My problem is that even if there is a change in this.props.roomQueue, the FlatList having its data={this.props.roomHosts} get's re-rendered.
How do i prevent this re-render to ensure that only if the FlatList's corresponding data changes, then only will it re-render, otherwise it won't. Do I have to change the way I store queue and hosts? or is there something else?

You can do this with using only one flatlist. Merge your both array's into one and show results from one list.. you can spare them in ui with a type.
This is a genuine procedure of what developers do, cz rendering 2 list in same page and same direction is accually no mean. Your query is valid.

You can add List ListFooterComponent and it will automatically do this for you
<FlatList
contentContainerStyle={{
width: WINDOW_WIDTH,
paddingVertical:WINDOW_WIDTH*0.2,
marginLeft:10
}}
ListFooterComponent={()=> returnYourViewDesignHere()}
columnWrapperStyle={{ flex: 1, justifyContent: "space-around" }}
keyExtractor={(item) => item.id}
onEndReached={() => getPaginationData()}
onEndReachedThreshold={0.0001}
numColumns={3}
showsVerticalScrollIndicator={false}
data={allShows}
renderItem={({ item, index }) => {
return (
<TouchableWithoutFeedback
key={item.index + Math.floor(Math.random() * 1000)}
onPress={() =>
props.navigation.navigate(
item.type == "movie" ? "MovieDetailScreen" : "SeasonDetail",
{
data: item,
object: {
id: item.id,
},
}
)
}
>
<View style={styles.boxContainer}>
<View style={styles.imageBackground}>
<Text style={styles.backgroundText}>KEIN</Text>
<Text
style={[styles.backgroundText, { color: COLOR.primary }]}
>
POSTER
</Text>
</View>
<Image
source={{
uri: item.coverUrl ? item.coverUrl : item.coverPath,
}}
style={styles.imageBox}
resizeMode={"stretch"}
/>
<Text
numberOfLines={2}
ellipsizeMode={"tail"}
style={styles.text}
>
{item.showTitle ? item.showTitle : item.title}
</Text>
{userWatchedList.some((uwl) => uwl.id == item.id) ? (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? removeFromUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={WATCHLIST_CHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
) : (
<TouchableWithoutFeedback
onPress={() =>
isloggedIn
? addToUserWatchList(item)
: handleModalVisibility()
}
>
<Image
source={CIRCLE_UNCHECKED}
style={{
width: 25,
height: 25,
position: "absolute",
right: 5,
top: 5,
}}
/>
</TouchableWithoutFeedback>
)}
</View>
</TouchableWithoutFeedback>
);
}}
/>

Related

React Native : Error in the data part of Flatlist

I tried using Flatlist in order to respond to one of my issue but I got an error on the data= of my Flatlist . I don't rreally undersstand the issue and the error message is not helping ( No overload matches this call. Overload 1 of 2, '(props: FlatListProps<any> | Readonly<FlatListProps<any>>): FlatList<any>', gave the following error. Type '{ id: string; name: string; data: string[]; description: string[]; }' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 19 more. )
I used flatlist because of this : React Native : Conditional Views.
Here is my code:
<View style={{ flex: 10}}>
{letter.map((letter) => {
const isleetergotdata = letter.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{letter.name}
</Text>
</div>
{letter.data?.map((item, index) => {
if (total !== same) {
return (
<FlatList
data={letter} // HERE IS THE ISSUE
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{letter.description[index]}</Text>
</View>
);
}}
/>
);
}
})}
</View>
);
})}
</View>
And here is my data:
const letter = [
{
id: "1",
name: "A",
data: ["Abricot", "Abricot", "Abricot"],
description: [
"Un abricot est un fruit",
"Un abricot est un fruit",
"Un abricot est un fruit",
],
},
//...
{
id: "5",
name: "E",
data: [],
description: [],
},
//...
];
The problem here is that you are providing letter to the FlatList's data prop, but this is not the global letter variable but the one of the local scope of the outer map function. You have given them the same name. Thus, letter in the map function is an object and not an array.
It seems to me that you want to create a FlatList for each object inside the "global" letter variable and provide the description property of each of these objects (which is an array and you are acessing it via the index which is really not necessary).
Thus, you actually only need to use one map and remove the inner map function. I would also rename the local variable letter to something else.
<View style={{ flex: 10}}>
{letter.map((datum) => {
const isleetergotdata = datum.data?.length > 0;
if (!isleetergotdata) {
return null;
}
return (
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<div
ref={fieldRef}
style={{
marginBottom: 100,
}}
>
<Text
style={{
fontSize: 90,
}}
>
{datum.name}
</Text>
</div>
if (total !== same) {
return (
<FlatList
data={datum.description}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
return (
<View>
<Text>{item}</Text>
</View>
);
}}
/>
);
}
)
}
</View>
);
})}
</View>
And to be precise here, remove the letter.data?.map((item, index) part completely.
If you need both data and desription in the same FlatList, then you can still leave the inner map function.
<FlatList
data={datum.data}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item, index }) => {
return (
<View>
<Text>{datum.description[index]}</Text>
</View>
);
}}
/>
You can get the data from array.
Step:01 Inside API call.
jsonElement["Letter"]
Step:02
const [dataSource, setDataSource] = useState([]);
Step:03
<FlatList horizontal={true} data={dataSource} />
Step:04
{item.id}
₹ {item.name}
When I run your code I face a different error related to the div tag. React-Native has no dom and if you do not have a custom 'div' component, you can not use the div tag. Even if a 'Div' component is in your project, component names should start with a capital letter.

Prevent Item Background Color From Changing Flatlist

I have a FlatList that is pulling in contact from a user's phone. I want to give each contact that doesn't have an avatar a random color from an array of hex color codes which I call "CircleColors".
This is what I'm trying:
<FlatList
data={filteredContacts}
renderItem={({ item }) => (
<View key={uuidv4()} style={styles.contactItem}>
<View style={item.avatar && styles.contactAvatar}>
{item.avatar && (
<Image
style={{
borderRadius: 50,
width: 50,
height: 50,
}}
source={{
uri: item.avatar,
}}
/>
)}
{!item.avatar && (
<ContactItem
itemData={item.name}
color={
CircleColors[
Math.floor(Math.random() * CircleColors.length)
]
}
/>
)}
</View>
<View>
<Text style={styles.contactTxt}>{`${item.name}`}</Text>
<Text
style={
item.usernames.length
? styles.contactUser
: styles.noUser
}
>
{item.usernames.length ? `$${item.usernames}` : null}
</Text>
</View>
</View>
)}
/>
And here is the code for the contact item:
const ContactItem = ({ itemData, color }) => {
return (
<View
style={[
styles.userCircle,
{
backgroundColor: color,
},
]}
>
<Text style={styles.userCircleTxt}>
{itemData.substr(0, 1).toUpperCase()}
</Text>
</View>
);
};
The problem is that on scroll, the flatlist rerenders each item, and the colors change each time. Is there a way to persist the original random color given to each item background, even on scroll?
Any help would be greatly appreciated.
Thanks!
the problem come with this line
<View key={uuidv4()} style={styles.contactItem}>
the key always change, you should pass index to key
renderItem={({ item,index }) => (
<View key={index} style={styles.contactItem}>
Pass key as index and dynamic background color styles in array like below -
renderItem={({ item,index }) => (
<View key={index} style={[{item.background},styles.contactItem]}>

React Native: RenderItem in FlatList. How to map array stored in local state?

I'm creating a small application with React Native that has a local SQLite database for storing images but having trouble rendering my array of images (fetched from the local database and stored in the local state).
Before I was rendered by mapping the data and that worked fine.
state = {
items:[when the user uses the app this array fills with images],
};
<ScrollView>
{items.map(({ id, value }) => (
<TouchableOpacity onPress={this.deletePhoto}
key={id}>
<Image source={{ uri: value }} style={{ width: null, height: 400 }} />
</TouchableOpacity>
))}
</ScrollView>
);
}
But now I would like to go a step further and render the data in a FlatList with my choice of formatting (a grid). Although I can get the FlatList to render the number of images within the array, I can't get the actual image to show. I'm not sure how to pass the data successfully?
renderItem = ({ id, value }) => {
const { items } = this.state;
if (items.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
}
return (
<TouchableOpacity style={styles.item} onPress={this.deletePhoto} key={id}>
<Image source={{ uri: value }} style={{ width: null, height: 400 }} />
</TouchableOpacity>
);
};
For context, this is the creation of the 'items' SQL Table with the 'id' and 'value' attributes:
componentDidMount() {
db.transaction(tx => {
tx.executeSql(
'create table if not exists items (id integer primary key not null, value text);'
);
});
}
I guess the question is, how do I pass/access the attributes of the items array into a functional component?
Update:
https://ibb.co/hMY1qBy (What I'm getting - e.g DB Entry creating a View but no image rendering)
https://ibb.co/RN4rqyK (What I'm getting from the answer below)
After getting a reproducible code from your repo that you shared I manage to render the images in the list.
Here is your renderItem function
renderItem = ({ item }) => {
const { items } = this.state;
if (items.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
}
return (
<TouchableOpacity style={styles.item} onPress={this.deletePhoto} key={item.id}>
<Image source={{ uri: item.value }} style={{ width: 400, height: 120 }} />
</TouchableOpacity>
);
};
You have to get the item from your params of the renderItem function as well you have to pass width to image for rendering, The above code is working fine.
The flatlist renderItem prop gets a argument with multiple properties the main ones will be item and index which are commonly used to access the item in of the row you can use item like below
<FlatList
data={items}
renderItem={({ item }) => (
<TouchableOpacity style={styles.item} onPress={this.deletePhoto} key={item.id}>
<Image source={{ uri: item.value }} style={{ width: null, height: 400 }} />
</TouchableOpacity>
)}
keyExtractor={item => item.id}
/>

How do I play video in react-native only when user sees the full video component?

I am using react-native-video-player to play video in my app. I have a screen that renders sub-components where, in each one of them I have a video player embedded. My question is, how do I only make a video play when the user sees the entirety of the component where the video is embedded? Otherwise a person would hear 10 videos playing simultaneously when entering the screen.
<FlatList
data={this.state.data}
style={{ marginTop: 10 }}
renderItem={({ item }) => (
<DiscoveryPanel
{...item}
componentId={this.props.componentId}
connectionType={this.state.connectionType}
followAction={() => this.followAction(item)}
/>
)}
keyExtractor={item => item.eid}
/>;
const DiscoveryPanel = ({ relevant }) => {
return (
<View style={styles.boxShadow}>
<View style={styles.topContainer}>
<VideoPlayer
thumbnail={{ uri: logo }}
video={{
uri: stream_urls["480p30"]
? stream_urls["480p30"]
: stream_urls["chunked"]
}}
muted={false}
pauseOnPress={true}
autoplay={connectionType == "wifi"}
/>
<Image
style={{ position: "absolute", height: 60, width: 60 }}
source={require("../../../assets/images/record_gif.gif")}
/>
</View>
</View>
);
};
I think there is a window property which tells you the current height. Just use this number in the scroll event

Checked - Unchecked doesn't working in ListView - React Native

friend I Will integrated checked - unchecked in listView. So that When user click on checked then store the data in Array and unchecked then i will remove the data. Its working fine, But the UI Will not updated after checked - unchecked.
<List containerStyle={{marginTop : 0 , borderBottomWidth : 0 , borderBottomColor : 'black', borderTopWidth : 0}}>
<FlatList
data={this.state.list}
renderItem={({ item }) => (
<ListItem containerStyle={{height: 80, backgroundColor : 'transparent', borderBottomWidth : 0, borderTopWidth : 0}}
title={
<View style={styles.titleView}>
<Text style={styles.ratingText}>{item.iWorkerID.vFirstName}</Text>
</View>
}
rightIcon={
<TouchableOpacity onPress = {() => this.selectedWorker(item)} style={{width: 30, height: 30 , marginTop : 10, marginRight : 30}}>
<Image style = {{width: 30, height: 30}} source={this.state.selectedList.includes(item) ? require("./Images/uncheckd.png") : require("./Images/checked.png")}/>
{/* {this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/uncheckd.png")}/>}
{!this.state.selectedList.includes(item) && <Image style = {{width: 30, height: 30}} source={require("./Images/checked.png")}/>} */}
</TouchableOpacity>
}
avatar={<Avatar
rounded
medium
containerStyle={{marginLeft: 30}}
source={{uri: Globle.IMAGE_URL+item.vProfileImage}}
activeOpacity={0.7}
/>}
/>
)}
/>
</List>
And on the check/uncheck button, I will add/remove object from array,
selectedWorker = (data) =>{
console.log('data is ', data);
if (!this.state.selectedList.includes(data)) {
// this.setState({ selectedList : [...this.state.selectedList , data]})
this.state.selectedList.push(data);
} else {
var index = this.state.selectedList.indexOf(data);
if (index > -1) {
this.state.selectedList.splice(index, 1);
}
}
this.setState({list : this.state.list})
console.log('selected list' , this.state.selectedList);
}
Main Issue : Want to update image checked/unchecked according to selectedList array, How can i Update item in listView.
What to do inside selectedWorker method.
GIF :
you are using Flatelist inside List, Both are a component to listing items. you can use List or Flatelist, not both.
I hope it will help you..
I try to make Demo similar to that you want.
constructor(props) {
super(props)
this.state = {
list: [
{
id: 1,
name: "Harpal Singh Jadeja",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 2,
name: "Kirit Mode",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 3,
name: "Rajiv Patil",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
},
{
id: 4,
name: "Chetan Doctor",
avtar: "https://cdn.pixabay.com/photo/2016/08/08/09/17/avatar-1577909_960_720.png"
}]
};
};
renderListItem = (index, item) => {
return (
<View style={styles.notification_listContainer}>
<Image source={{uri: item.avtar, cache: 'force-cache'}}
style={circleStyle}/>
<View style={{flex: 1, paddingLeft: 10, justifyContent: 'center'}}>
<Label roboto_medium
align='left'
color={Color.TEXT_PRIMARY}
numberOfLines={1}>
{item.name}
</Label>
<Label roboto_medium
small
align='left'
color={Color.TEXT_SECONDARY}
mt={8}>
Programmer
</Label>
</View>
<View style={{justifyContent: 'center'}}>
<TouchableHighlight
style={{
backgroundColor: item.isSelected ? Color.BLACK : Color.TEXT_SECONDARY,
alignItems: 'center',
justifyContent: 'center',
height: 40,
width: 40,
borderRadius: 20
}}
onPress={this.onSelectWorker.bind(this, index, item)} underlayColor={Color.BLACK}>
<Icon name='done'
size={20}
color={Color.WHITE}/>
</TouchableHighlight>
</View>
</View>
);
};
onSelectWorker = (index, item) => {
console.log("Selected index : ", index);
let tempList = this.state.list;
tempList[index].isSelected = tempList[index].isSelected ? false : true
this.setState({
list: tempList
});
};
render() {
return (
<View style={styles.notification_Container}>
<FlatList
data={this.state.list}
renderItem={
({index, item}) => this.renderListItem(index, item)
}
keyExtractor={item => item.id}
extraData={this.state}
/>
</View>
)
}
You need to add a key to your ListItem which is based on a unique id of the item, so that React can distinguish between the items rendered.
When you use index of an array as a key, React will optimize and not render properly. What happens in such a scenario can be explained with an example.
Suppose React renders an array of 10 items and renders 10 components. Suppose the 5th item is then removed. On the next render React will receive an array of 9 items and so React will render 9 components. This will show up as the 10th component getting removed, instead of the 5th, because React has no way of differentiating between the items based on index.
Therefore always use a unique identifier as a key for components that are rendered from an array of items.

Resources