React Native: FlatList Opens Modal for all Items Instead of Selected Item

Viewed 1966

I am using React Native FlatList and React Native Modal.

Upon clicking on the item from the FlatList, I want to view 1 Modal only (containing the details of the item selected).

However, if there are 4 items in the FlatList, selecting 1 item causes all 4 modals to pop up.

Is there anyway I can display only 1 modal for 1 selected item in the FlatList instead of multiple modal?

Code Snippet below (some lines of code were removed as it's not needed):

    constructor(props) {
            super(props);
            this.state = {
                dataSource: [],
                isLoading: true,
                modalVisible: false,
            }
        }
    
    setModalVisible = (visible) => {
            this.setState({ modalVisible: visible });
        } 
        
    
        viewModal(item, price) {
            const { modalVisible } = this.state;
            return (
                <Modal
                    statusBarTranslucent={true}
                    animationType={"slide"}
                    transparent={true}
                    visible={modalVisible}
                    onRequestClose={() => {
                        Alert.alert("Modal has been closed.");
                    }}
                >
                    <View>
                        <View>
                            <View>
                                <Text>
                                    Appointment Start Time:
                                </Text>
                                <Text>
                                    {moment(item.time_start).format('h:mm a')}
                                </Text>
                            </View>
    
                            <View>
                                <Text>
                                    Appointment End Time:
                                </Text>
                                <Text>
                                    {moment(item.end_start).format('h:mm a')}
                                </Text>
                            </View>
    
                            
    
                            
                            <View style={styles.row}>
                                <Text>
                                    Total:
                                </Text>
                                <Text>
                                    {price}
                                </Text>
                            </View>
                            
                            <View>
                                <View>
                                    <Button
                                        mode="outlined"
                                        onPress={() => {
                                            this.setModalVisible(!modalVisible);
                                        }}
                                    >
                                        {'Cancel'}
                                    </Button>
                                </View>
                                
                                <View>
                                    <Button
                                        mode="contained"
                                        onPress={() => {
                                            this.setModalVisible(!modalVisible);
                                        }}
                                    >
                                        {'Accept'}
                                    </Button>
                                </View>
                            </View>
                        </View>
                    </View>
                </Modal>
            );
        }
    
        viewFreelancerTime() {
            return (
                <View>
                    <FlatList
                        renderItem={({ item }) => {
                            let totalPrice = (parseFloat(item.service_price) + parseFloat(item.service_deposit)).toFixed(2);
                            return (
                                <Container>
                                    {this.viewModal(item, totalPrice)}
                                    <TouchableNativeFeedback
                                        onPress={() => {
                                            this.setModalVisible(true);
                                        }}
                                    >
                                        <View>
                                            <View>
                                                <Text>
                                                    {moment(item.time_start).format('h:mm a')}
                                                </Text>
                                            </View>
    
                                            <View>
                                                <Text>
                                                    {totalPrice}
                                                </Text>
                                            </View>
                                        </View>
                                    </TouchableNativeFeedback>
                                </Container>
                            );
                        }}
                    />
                </View>
            );
        }

render() {
            return (
                <>
                    <View style={{ flex: 1 }}>
                        {this.viewFreelancerTime()}
                    </View>

                </>
            );
    };
1 Answers

The poblem is that you are rendering the modal in the renderItem method, so every time you select an item, the modal will open in each rendered item.

To solve that you will have to render a custom Modal component with an absolute position at the same level of your FlatList, and pass the selected item information as props.

UPDATE

Just something like this:

import React, {useState} from "react";
import { Modal } from "react-native";

export default function MyFlatList(props) {
  const [selectedItem, setSelectedItem] = useState(null);

  const handleOnSelectItem = (item) => {
    setSelectedItem(item);
  };

  const handleOnCloseModal = () => {
    setSelectedItem(null);
  };

  renderItem = ({ item }) => {
    return (
      <Container>
        <TouchableNativeFeedback onPress={() => handleOnSelectItem(item)}>
          <View>
            <View>
              <Text>{moment(item.time_start).format("h:mm a")}</Text>
            </View>

            <View>
              <Text>{totalPrice}</Text>
            </View>
          </View>
        </TouchableNativeFeedback>
      </Container>
    );
  };

  return (
    <View>
      <FlatList renderItem={this.renderItem} />
      <CustomModal isVisible={selectedItem} selectedItem={selectedItem} onClose={handleOnCloseModal} />
    </View>
  );
}

export function CustomModal(props) {
  const { isVisible, item, onClose,  /*...*/ } = props;

  // Play with the item data
  let totalPrice = (
    parseFloat(item.servicePrice) + parseFloat(item.serviceDeposit)
  ).toFixed(2);

  return <Modal visible={isVisible} onRequestClose={onClose}>{/*...*/}</Modal>; // Render things inside the data
}

I suggest you to do a pagination and play with FlatList native props if you are going to implement an infinite scroll.

Pd: to reduce re-renders because of state updates, I am reusing the selectedItem state, so if it is not null then the modal will be visible

Related