Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React native - dynamically add a view onPress

I have a View that contains a button - onPress it opens a modal showing a list of contacts.

  1. onPress of any of those contacts (pickContact function) I would like to dynamically add a new View to _renderAddFriendTile (above button).

  2. 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
like image 987
John Avatar asked Feb 06 '17 22:02

John


1 Answers

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>
    )
}
like image 116
rgommezz Avatar answered Sep 22 '22 22:09

rgommezz