We created a FlatList component and a Contact screen in our app. We want to add 'heart' icon to the near of the images in Contact screen. We added heart icon near to all of items. But, if we pressed one of these icons, it changes all of theirs colors to red, not only one of them. We want to change the color of clicked item.
Screenshot of our program:
This is our FlatList component:
import React, { Component } from 'react';
import { View, Text, SafeAreaView, StyleSheet, FlatList, Image, TouchableOpacity,
TouchableWithoutFeedback, TextInput } from 'react-native';
import { Right, Icon } from 'native-base';
import data from '../../data';
export default class FlatListComponent extends Component {
state = {
text: '',
contacts: data,
like: false,
color: 'white',
}
toggleLike=()=>{
this.setState({
like: !this.state.like
})
if(this.state.like){
this.setState({
color: 'red',
})
}else{
this.setState({
color: 'white',
})
}
}
renderContactsItem = ({item, index}) => {
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={this.toggleLike}>
{/*{this.like ? (
<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'red'}} />
) :
( <Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: 'white'}} /> )
}*/}
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.color === "white" ? 'white' :'red', paddingRight: 10 }}/>
{/*<Icon name="heart" type='FontAwesome' style={{paddingRight: 10, fontSize: 30, color: this.state.color}} />*/}
</TouchableWithoutFeedback>
</Right>
</View>
);
}
searchFilter = text => {
const newData = data.filter(item => {
const listItems = `${item.first_name.toLowerCase()}`
return listItems.indexOf(text.toLowerCase()) > -1;
});
this.setState({
contacts: newData,
});
};
renderHeader = () => {
const {text} = this.state;
return (
<View style={styles.searchContainer}>
<TextInput
onChangeText = {text => {
this.setState ({
text,
});
this.searchFilter(text);
}}
value={text}
placeholder="Search..."
style={styles.searchInput} />
</View>
)
}
render() {
return (
<FlatList
ListHeaderComponent={this.renderHeader()}
renderItem={this.renderContactsItem}
keyExtractor={item => item.id}
data={this.state.contacts}
/>
);
}
}
const styles = StyleSheet.create({
itemContainer: {
flex: 1,
flexDirection: 'row',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee'
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
marginHorizontal: 10,
},
textContainer: {
justifyContent: 'space-around',
},
name: {
fontSize: 16,
},
searchContainer: {
padding: 10
},
searchInput: {
fontSize: 16,
backgroundColor: '#f9f9f9',
padding: 10,
}
});
Our Contact screen is just:
import React from 'react';
import 'SafeAreaView' from 'react-native';
import FlatList from './FlatList';
export default function Contact() {
<SafeAreaView style={{ flex: 1 }}>
<FlatList />
</SafeAreaView>
}
How can we implement this?
I've run into this recently :) One option is to make the renderContactsItem
its own component. For example:
const RenderContactsItem = ({item, index}) => {
const [like, setLike] = useState(false);
const [color, setColor] = useState("white");
const toggleLike = () => {
setLike(!like)
if(like) {
setColor("red");
} else {
setColor("white");
}
}
return (
<View style={[styles.itemContainer]}>
<Image
style={styles.avatar}
source={{ uri: item.image }} />
<View style={styles.textContainer}>
<Text style={[styles.name], {color: '#fafafa'}}>{item.first_name}</Text>
<Text style={{ color: '#fafafa' }}>{item.last_name}</Text>
</View>
<Right style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback onPress={toggleLike}>
<Icon name='heart' type='FontAwesome' size={32} style={{color, paddingRight: 10 }}/>
</TouchableWithoutFeedback>
</Right>
</View>
);
}
In this case, each item manages its own state, so setting like
doesn't set it for every item.
Another option would be to build an object with "like" states and set the values as the hearts are pressed. For example:
state = {
text: '',
contacts: data,
like: {},
color: 'white', // You don't need this
}
Then, when a heart is pressed, you can send toggleLike
the index
, and set the state like so:
toggleLike = (index) => {
let newLike = {...this.state.like};
newLike[index] = !Boolean(newLike[index]);
this.setState({
like: newLike,
});
}
And render the color conditionally depending on the index value of the like
state, like so:
<Icon name='heart' type='FontAwesome' size={32} style={{color: this.state.like[index] ? 'red' :'white', paddingRight: 10 }}/>