I have a View that contains a button - onPress it opens a modal showing a list of contacts.
onPress of any of those contacts (pickContact function) I would like to dynamically add a new View to _renderAddFriendTile (above button).
Also ideally, the 'Add' icon next each contact name (in the modal) should update ('Remove' icon) whether or not they are present in _renderAddFriendTile View.
What would be the best way to do it?
[UPDATED code]
import React, {Component} from 'react'
import {
Text,
View,
ListView,
ScrollView,
StyleSheet,
Image,
TouchableHighlight,
TextInput,
Modal,
} from 'react-native'
const friends = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
}).cloneWithRows([
{
id: 1,
firstname: 'name01',
surname: 'surname01',
image: require('../images/friends/avatar-friend-01.png')
},
{
id: 2,
firstname: 'name02',
surname: 'surname02',
image: require('../images/friends/avatar-friend-02.png')
},
{
id: 3,
firstname: 'name03',
surname: 'surname03',
image: require('../images/friends/avatar-friend-03.png')
},
{
id: 4,
firstname: 'name04',
surname: 'surname04',
image: require('../images/friends/avatar-friend-04.png')
},
])
class AppView extends Component {
state = {
isModalVisible: false,
contactPicked: [],
isFriendAdded: false,
}
setModalVisible = visible => {
this.setState({isModalVisible: visible})
}
pickContact = (friend) => {
if(this.state.contactPicked.indexOf(friend) < 0){
this.setState({
contactPicked: [ ...this.state.contactPicked, friend ],
})
}
if(this.state.contactPicked.indexOf(friend) >= 0){
this.setState({isFriendAdded: true})
}
}
_renderAddFriendTile = () => {
return(
<View style={{flex: 1}}>
<View style={[styles.step, styles.stepAddFriend]}>
<TouchableHighlight style={styles.addFriendButtonContainer} onPress={() => {this.setModalVisible(true)}}>
<View style={styles.addFriendButton}>
<Text style={styles.addFriendButtonText}>Add a friend</Text>
</View>
</TouchableHighlight>
</View>
</View>
)
}
render(){
return (
<ScrollView style={styles.container}>
<Modal
animationType={'fade'}
transparent={true}
visible={this.state.isModalVisible}
>
<View style={styles.addFriendModalContainer}>
<View style={styles.addFriendModal}>
<TouchableHighlight onPress={() => {this.setModalVisible(false)}}>
<View>
<Text style={{textAlign:'right'}}>Close</Text>
</View>
</TouchableHighlight>
<ListView
dataSource={friends}
renderRow={(friend) => {
return (
<TouchableHighlight onPress={() => {this.pickContact()}}>
<View style={[styles.row, styles.friendRow]}>
<Image source={friend.image} style={styles.friendIcon}></Image>
<Text style={styles.name}>{friend.firstname} </Text>
<Text style={styles.name}>{friend.surname}</Text>
<View style={styles.pickContainer}>
<View style={styles.pickWrapper}>
<View style={this.state.isFriendAdded ? [styles.buttonActive,styles.buttonSmall]: [styles.buttonInactive,styles.buttonSmall]}>
<Image source={this.state.isFriendAdded ? require('../images/button-active.png'): require('../images/button-inactive.png')} style={styles.buttonIcon}></Image>
</View>
</View>
</View>
</View>
</TouchableHighlight>
)
}}
/>
</View>
</View>
</Modal>
{this._renderAddFriendTile()}
</ScrollView>
)
}
}
export default AppView
Since you need to update something dynamically, that's a clear indication you need to make use of local state for modelling that data. (Setting aside you are not using a state library management like Redux)
state = {
isModalVisible: false,
contactPicked: null,
}
Your pickContact
function needs the friend data from the listView, so you need to call it with the row selected:
<TouchableHighlight onPress={() => {this.pickContact(friend)}}>
Then inside your pickContact
function, update your UI with the new contactsPicked
data model
pickContact = (friend) => {
this.setState({
contactPicked: friend,
});
}
That will make your component re-render and you can place some logic inside _renderAddFriendTile
to render some extra UI (Views, Texts..) given the existence of value on this.state.contactPicked
You could use something along these lines:
_renderAddFriendTile = () => {
return(
<View style={{flex: 1}}>
{this.state.contactPicked && (
<View>
<Text>{contactPicked.firstName}<Text>
</View>
)}
<View style={[styles.step, styles.stepAddFriend]}>
<TouchableHighlight style={styles.addFriendButtonContainer} onPress={() => {this.setModalVisible(true)}}>
<View style={styles.addFriendButton}>
<Text style={styles.addFriendButtonText}>Add a friend</Text>
</View>
</TouchableHighlight>
</View>
</View>
)
}
Notice you now hold in state the firstName of the contact picked, so point number 2 should be easy to address. Just inside renderRow
of ListView, render a different Icon if friend.firstname === this.state.contactPicked.firstname
Note: Don't rely on firstname for this sort of check since you can have repeated ones and that would fail. The best solution is to provide to your list model an unique id
property per contact and use that id
property for checking the logic above.
Side Notes
The part {this.state.contactPicked && (<View><Text>Some text</Text></View)}
is using conditional rendering. The &&
acts as an if so if this.state.contactPicked
is truthy, it will render the view, otherwise it will render nothing (falsy and null
values are interpreted by react as "nothing to render").
Of course, if you want to render more than one item dynamically, your state model should be an array instead, i.e contactsPicked
, being empty initially. Every time you pick a contact you'll add a new contact object to the array. Then inside _renderAddFriendTile
you can use map to dynamically render multiple components. I think you won't need a ListView
, unless you wanna have a separate scrollable list.
_renderAddFriendTile = () => {
return(
<View style={{flex: 1}}>
{this.state.contactsPicked.length && (
<View>
{this.state.contactsPicked.map(contact => (
<Text>{contact.firstName}</Text>
)}
</View>
)}
<View style={[styles.step, styles.stepAddFriend]}>
<TouchableHighlight style={styles.addFriendButtonContainer} onPress={() => {this.setModalVisible(true)}}>
<View style={styles.addFriendButton}>
<Text style={styles.addFriendButtonText}>Add a friend</Text>
</View>
</TouchableHighlight>
</View>
</View>
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With