React Native FlatList: render conditional style with dynamic data - javascript

I am new to React native and implementing FlatList where I am facing some issues with dynamic data. I have a list of total seats and booked seats.
this.state = {
seats: props.route.params.seats,
reservedSeats: props.route.params.Reserved,
date: new Date()
}
Following is the FlatList i have implemented
<FlatList style={styles.flatListArea1}
contentContainerStyle={{margin:0}}
data={this.state.seats}
numColumns={4}
showsHorizontalScrollIndicator={false}
renderItem={({item}) =>
<View style={styles.containerIcons} key={item}>
<TouchableOpacity style={this.state.selectedItem === item ? styles.menuSelected : styles.menuTop } onPress={ () => this.selectSeat(item)}>
<View style={styles.buttons}>
<Text style={styles.HallsText} key={item.key}>{item.Id}</Text>
</View>
</TouchableOpacity>
</View>}
/>
On Click event, I am able to change the color. I appreciate if someone can help to understand, How we can re-render FlatList based on dynamic data presented in reserved state. For example. for today, out of 5 seats 3 are booked. Those should be gray and others should be red.
I appreciate your help on this.
Regards,

You firstly need a method that tells if a seat is available or not.
isReserved = (item) => {
return this.state.reservedSeats.filter(seat => seat.Id ==item.Id).length > 0;
}
Then you change the appearance of your list like this
<FlatList
style={styles.flatListArea1}
contentContainerStyle={{ margin: 0 }}
data={this.state.seats}
numColumns={4}
showsHorizontalScrollIndicator={false}
renderItem={({ item, index }) => (
<View style={[styles.containerIcons, { backgroundColor: isReserved(item) ? "#FAFAFA" : "white" }]} key={index}>
<TouchableOpacity
style={this.state.selectedItem === item ? styles.menuSelected : styles.menuTop}
onPress={() => this.selectSeat(item)}
disabled={isReserved(item)}
>
<View style={styles.buttons}>
<Text
style={[styles.HallsText, { backgroundColor: isReserved(item) ? "#CCC" : "white" }]}
key={item.key}
>
{item.Id}
</Text>
</View>
</TouchableOpacity>
</View>
)}
/>;
Pay attention to the key attribute

Related

Selecting multiple buttons from map function in react native

I'm trying to select multiple options from the map function but i cant seem to generate logic to do so, i'm selecting a button and that button is generating a heading and a text felid in a scrollview with the lable of button as heading, but now i want to select multiple buttons to generate multiple components:
Any logic and help would be great please, m stuck!
here is code for refrence:
this is usestate and buttonLabels array:
const handlePress = (index) =>
{
setSelectedButtonIndex(index === selectedButtonIndex ? null : index);
};
const buttonLabels = [
'Homeowner',
'In-depth',
'Inherited',
'Probate',
'Lost income',
'Relocation',
'Divorce',
'Downsizing'
];
and here is the map function:
{/* List of Items */}
<View style={styles.buttonGrid}>
{buttonLabels.map((label, index) => (
<TouchableOpacity
key={index}
style={[
styles.button,
{ backgroundColor: index === selectedButtonIndex ? '#346284' : '#FFFFFF' },
]}
onPress={() => handlePress(index)}
>
<Text style={{ color: index === selectedButtonIndex ? '#FFFFFF' : '#888889' }}>
{label}
</Text>
</TouchableOpacity>
))}
</View>
and here i'm generate component at the selectedbutton
<ScrollView style={{flexGrow:2}}>
{selectedButtonIndex !== undefined && (
<View style={styles.headingContainer}>
<View style={styles.heading}>
<Text style={styles.headingText}>{buttonLabels[selectedButtonIndex]}</Text>
<TouchableOpacity onPress={() => setSelectedButtonIndex(undefined)}>
<Image source={require('../assets/Icons/close.png')} style={styles.closeIcon} />
</TouchableOpacity>
</View>
<TextInput style={styles.textInput} placeholder="Enter Text Here" />
</View>
)}
</ScrollView>
if you want to choose multiple item,
let array=[{"option":"option1",selected:false}, {"option":"option1",selected:false}]
based on index or item you need to change the boolean value, then show when the boolean value is true show the item example ,
onpress function like
const onselect=(selectedIndex)=>{
let temp=array //local array
temp.map((item,index)=>{
if(index==selectedIndex) item.selected=!item.selected
})
setState([...temp]) //set your local state
})
in render method,
array.map((item,index)=>{if(item.selected) <View></View>})

key should be unique react native error while mapping data array?

I have a listing component in my react native map. in which I'm mapping the list of items in the component. How the problem is. I have been getting warnings that each child should have a unique key. Even though I have given them a unique key but still getting that warning.
here is the code.
<ScrollView horizontal>
{this.state.isLoading === true ? (
[...Array(7)].map((item, index) => <PremiumShimmer key={index} />)
) : this.state.isError === true ? (
<View>
<Text style={{ color: "black" }}>
Facing some issue to load data from server
</Text>
</View>
) : this.state.Data.length === 0 ? (
<Text style={{ color: "black" }}>No Data Founbd</Text>
) : (
this.state.Data.map((item) => (
this.ItemRenderer({item})
))
)}
</ScrollView>
ItemRenderer ({ item }) {
return (
<PremiumItemListComponent item={item} navigation={this.props.navigation} />
);
};
PremiumItemListComponent
<TouchableOpacity
style={styles.listBox}
onPress={() => this.props.navigation.navigate("Detail", { data: this.props.item })}
>
<ImageBackground style={styles.listImage} imageStyle={{ borderRadius: 26}} source={{uri:SERVER_URL.apiUrl+this.props.item.background}}>
<Text style={{color:"white",fontSize:13,paddingLeft:10,fontWeight: 'bold'}}>{this.props.item.name}</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap',padding:10}}>
{
[...Array(5)].map((e, i) =>
// 3.688689 will be replaced by rating
// console.log(typeof i,typeof 4, i+1<=4)
<Ionicons name={i+1<=Number((3.688689).toFixed(0))?"star-sharp":"star-outline"} size={15} color="#FFE600" />
)
}
</View>
</ImageBackground>
</TouchableOpacity>
I have console logged item.ids and they are unique 23, 24, and 25.
if You need more code to help. You can ask. Thanks in Advance.
Your code looks right, i don't think the warning you are getting for key is from this specific component, but i would like to add a few things to make your life a little easier.
Never use Index as key. Index will change if your data changes which will eventually make the purpose of keys useless.
Wherever possible use FlatList Component instead of a map since you get a easy keyExtractor Prop and it increases your performance if the list is long
Example : -
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
using the keyExtractor Prop you can pick out and convert a unique value from your data into a key and it automatically adds the key prop into rendered component wrapper.

React Native touchable opacity pressing after 2 to 5 seconds inside a FlatList

Good day,
I'm working on a small react native project, I'm getting data from an API, but I noticed that every time I populate the state with data from the API, the mobile app becomes slow, and when I press the Touchable Opacity inside a child element of that Flatlist it takes around 5 seconds to respond.
//Don't worry it's all wrapped in a parent container
<View style={globalStyles.searchContainer}>
<TextInput style={globalStyles.textInput} placeholder="Who are you looking for? (e.g. Plumber)" onChangeText={setJob}/>
<TouchableOpacity style={globalStyles.searchButton} onPress={handleSearch}>
<Text style={globalStyles.text}>Search</Text></TouchableOpacity>
</View>
<FlatList
data={workers}
keyboardShouldPersistTaps="handled"
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<View style={globalStyles.workerCard}>
<Image
style={globalStyles.thumbnail}
source={{uri: globalConfig.api_url + item.profile_picture}}
/>
<View style={globalStyles.workerDetails}>
<Text style={globalStyles.textDetails}>Name: {`${item.first_name} ${item.surname}`}</Text>
<Text style={globalStyles.textDetails}>Gender: {item.gender}</Text>
<Text style={globalStyles.textDetails}>Job: {item.job}</Text>
<Text style={globalStyles.textDetails}>Price: {`R${item.price}`}</Text>
<Text style={globalStyles.textDetails}>Transport fee: {`R${item.transport}`}</Text>
<Text style={globalStyles.textDetails}>Other Job: {(item.service1) ? item.service1 : ""} </Text>
<Text style={globalStyles.textDetails}>Other Job: {(item.service2) ? item.service2 : ""} </Text>
<Text style={globalStyles.textDetails}>Online: {(item.available) ? "Yes" : "No"} </Text>
<TouchableOpacity style={globalStyles.searchButton} onPress={() => bookWorker(item)}>
<Text style={globalStyles.text}>Book</Text>
</TouchableOpacity>
</View>
</View>
)}
/>
Add an "extraData" property to your flatList. This will tell your flatList to re-render on data changes. (https://reactnative.dev/docs/flatlist#extradata)
<FlatList
data={workers}
keyboardShouldPersistTaps="handled"
keyExtractor={(item, index) => index.toString()}
extraData = {this.state.refreshing}
renderItem={({ item }) => ...
Here "this.state.refreshing" could be a boolean or an incremented integer that is updated each time you call the api.
Alternatively, and most recommended, you could add a onRefresh property and move your api calls there. (https://reactnative.dev/docs/flatlist#onrefresh). This would handle all the flatList changes for you once you update your "workers" array.
I figured it out, there was a function that was being called somewhere in the component, I removed it and now it's working just fine.

Adding delete button in flat list react native

I am trying to add one delete button at end of each list in flat list. Expected behaviour is to delete the list from the array.So i added on press method and called on alert to check wether its working fine or not.
Now when ever i am adding one items to my flat list.This alert pop up is coming. But on click my delete button pop up is not showing. Not sure whether i added in correct place or not.
I am new learner to react native.Please help.
<FlatList
data={data.ingredientArray}
width='100%'
extraData={data.ingredientArray}
keyExtractor={(index) => index.toString()}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={({ item }) =>
<View style={styles.TextViewStyle}>
<View style={styles.row}>
<View style={styles.bullet}>
<Text style={{ fontWeight: 'bold', fontSize: 16 }}>{'\u2022' + " "}</Text>
</View>
<View style={styles.bulletText}>
<Text style={{ fontWeight: 'normal', fontSize: 16, color: '#009387' }}>{item.title}</Text>
</View>
<View>
</View>
<Feather
name="x-circle"
color="#009387"
size={30}
onPress={showAlert}
/>
</View>
</View>
} />
My alert :
const showAlert = () => {
alert('alert me');
}
Any help on this ?
You can change the onPress={showAlert} to onPress={() => showAlert()} 👍🏽

Show Empty list message only after Loader get ended in React Native using Flat list

I have a flat list, which gets its data source as a state. Actually, this data is from firebase, and i have been using redux. So, the data is fetched in the actions, and using callback i get the data to state.
What i want to achieve is, when there is no data found from the api, An empty list message should be show in the view. Actually , i achieved this using "ListEmptyComponent". But whats happening is the screen starts with empty message, and the spinner loads below it, and then if data found the message goes away as well as spinner.
But, what i wanted is, when the view gets rendered the first thing everyone should see is the spinner, and then if data empty spinner hides then empty list message displays.
How to achieve this ?
My Action :
export const fetchOrderHistory = (phone, callback) => {
return (dispatch) => {
dispatch({ type: START_SPINNER_ACTION_FOR_ORDER_HISTORY })
firebase.database().ref('orders/'+phone)
.on('value', snapshot => {
const snapShotValue = snapshot.val();
callback(snapShotValue);
dispatch ({ type: ORDER_HISTORY_FETCHED , payload: snapshot.val()});
dispatch({ type: STOP_SPINNER_ACTION_FRO_ORDER_HISTORY })
});
};
};
My Flat List & spinner:
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card
containerStyle={{ borderRadius: 5 }}
>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }
>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</Card>
)}
/>
{this.props.isSpinnerLoading &&
<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> }
My Call back at componentWillMount which set state:
componentWillMount() {
this.props.fetchOrderHistory((this.props.phone), (snapShotValue)=> {
const userOrderHistory = _.map(snapShotValue, (val,uid) => ({uid, ...val}))
this.setState({ historyOfOrders: userOrderHistory })
});
}
My EmptyList Message:
onListEmpty = () => {
return <View style={{ alignSelf: 'center' }}>
<Text style={{ fontWeight: 'bold', fontSize: 25 }}>No Data</Text>
</View>
}
My State:
state = { historyOfOrders: "" }
I am getting the spinner values from the reducers, using mapStateToProps.
Kindly Guide me, through
you have to do two things for that.
First, show Flatlist only if the loader is stopped. Second, set default value of this.state.historyOfOrders is null and check if this.state.historyOfOrders not null then only show Flatlist.
Here is a code:
{(!this.props.isSpinnerLoading && this.state.historyOfOrders != null) ?
(
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card containerStyle={{ borderRadius: 5 }}>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</Card>
)}
/>
) : null
}
With this condition, even if you want loader above Flatlist you can do that.
The path you should take is rendering only the spinner when the loading flag is set and rendering the list when loading flag is false.
Your render method should be like below
render()
{
if(this.props.isSpinnerLoading)
{
return (<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> );
}
return (/** Actual List code here **/);
}

Categories