Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to open Modal with item information when clicking a FlatList element?

What I am trying to achieve is to open a modal dialog when the user press on one of the items of a FlatList component. The code of this list looks like this:

class MyItem extends React.Component { 
  _onPress = () => { 
    this.props.onPressItem(this.props.item); 
  }; 
  render() { 
    return(
        <TouchableOpacity 
            {...this.props}
            onPress={this._onPress}
            >
            <Text style={styles.itemText}> {this.props.item.name}</Text>
        </TouchableOpacity>
    ) 
  } 
}

export default class MyList extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            data: {}, // some data correctly loaded
            isModalVisible: false
        };
    };

    _onPressItem = (item) => { 
        this._showModal;
    };

    _showModal = () => this.setState({ isModalVisible: true })

    _keyExtractor = (item, index) => item.id;

    _renderItem = ({item}) => (
        <MyItem 
            style={styles.row}
            item={item}
            onPressItem={this._onPressItem}
        />
    );

    render() {
      return(
        <KeyboardAvoidingView behavior="padding" style={styles.container}>
          <View style={styles.titleContainer}>
              <Text style={styles.title}>Tittle</Text>
          </View>
          <ScrollView style={styles.container}>
              <FlatList 
                  data={this.state.data}
                  ItemSeparatorComponent = {this._flatListItemSeparator}
                  renderItem={this._renderItem}
                  keyExtractor={this._keyExtractor}
              />
          </ScrollView>
          <MyModal modalVisible={this.state.isModalVisible}/>
        </KeyboardAvoidingView>
      );
    }
}

Styles, FlatList data and some functions have been removed because they are not relevant for this issue.

As you can see, MyModal component is declared after ScrollView component. The code is based on the use of react-native Modal component:

export default class MyModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
        isModalVisible: props.modalVisible
    };
  };

  _setModalVisible(visible) {
    this.setState({modalVisible: visible});
  }

  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.state.modalVisible}
            onRequestClose={() => {alert("Modal has been closed.")}}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this._setModalVisible(false) }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: 'transparent',
  },
  innerContainer: {
    borderRadius: 10,
    alignItems: 'center',
    backgroundColor: '#34495e',
 },
 buttonContainer: {
    paddingVertical: 15,
    marginTop: 20,
    backgroundColor: '#2c3e50',
    borderRadius: 15
 },
 buttonText: {
    textAlign: 'center',
    color: '#ecf0f1',
    fontWeight: '700'
 },
});

Current behaviour is MyModal component being displayed when I access to MyList component for the first time, I can close it and then the FlatList is there but when a list item is pressed, MyModal component is not displayed.

How can I manage to hide Modal and open it only when a list item is pressed?

Another doubt related to this is:

How to pass the pressed item object to MyModal component?

Thanks in advance!

like image 317
juankysmith Avatar asked Jan 30 '23 07:01

juankysmith


1 Answers

Passing the item through to the modal

To pass the selected item to your modal, you'll need to add it as a prop on your Modal component.

You can remember the selected item in MyList's state:

_onPressItem = (item) => { 
    this._showModal(item);
};

_showModal = (selectedItem) => this.setState({ isModalVisible: true, selectedItem })

And then pass it through to the modal when you render it from MyList:

// ...
      </ScrollView>
      <MyModal 
        modalVisible={this.state.isModalVisible} 
        selectedItem={this.state.selectedItem} />
    </KeyboardAvoidingView>
// ...

Controlling the modal's visibility

Currently you have a modal visibility boolean in both MyList's state (isModalVisible, which is passed through to MyModal as the modalVisible prop) and also in MyModal's state (modalVisible). There's no need for the last one - it'll just give you a headache trying to keep them in sync. Just use the props to "control" MyModal, keeping a single source of truth, and pass a callback to allow MyModal to tell MyList that the modal should be dismissed.

// ...
      </ScrollView>
      <MyModal 
        modalVisible={this.state.isModalVisible} 
        selectedItem={this.state.selectedItem}
        onDismiss={this._hideModal} />
    </KeyboardAvoidingView>
// ...

A new stateless MyModal:

export default class MyModal extends Component {    
  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.props.modalVisible}
            onRequestClose={() => { this.props.onDismiss() }}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this.props.onDismiss() }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}
like image 169
Rob Hogan Avatar answered Jan 31 '23 22:01

Rob Hogan