Venkat Sai
Venkat Sai

Reputation: 77

How to Pass Props to Modal Component in React Native

I need to pass Flatlist data to modal in react native. Like, on click the item from the flatlist, it shows the modal with data for selected item. Here is my code below

Home.js

import React, { Component } from 'react';
import { StyleSheet, Text, View, FlatList, Image, TouchableOpacity, Alert, Button } from 'react-native';
import { Popup } from './Modal';
import * as productdata from '../data/productdata.js';

export default class Home extends Component {

    constructor(props) {
        super(props);

        this.state = {
            isLoading: true,
            dataSource: [],
        };
    }

    componentDidMount() {
        this.setState({
            isLoading: false,
            dataSource: productdata.product,
        })
    }

    popupRef = React.createRef()
    onShowPopup = item => {   
       popupRef.show();  
    }
    onClosePopup = () => {
        popupRef.close()
    }


    ProductList = ({ item }) => (
        <View style={styles.listItem}>

            <TouchableOpacity onPress={() => this.onShowPopup(item)} style={{ height: 100, width: 100, justifyContent: "center", alignItems: "center" }}>
                <Image source={item.photo} style={{ width: 100, height: 100, borderRadius: 15 }} />
            </TouchableOpacity>
            <View style={{ alignItems: "center", flex: 1, marginTop: 20 }}>
                <Text style={{ fontWeight: "bold", fontSize: 22 }}>{item.name}</Text>
                <Text style={{ fontSize: 18, fontWeight: "bold" }}>{item.price}</Text>
            </View>
            <Popup name="Product Details" ref={(target) => popupRef = target} onTouchOutside={this.onClosePopup} />

        </View>
    );

    render() {
        return (
            <View style={styles.container}>

                <FlatList
                    data={this.state.dataSource}
                    renderItem={this.ProductList}
                    keyExtractor={item => item.name}
                />
            </View>
        );
    }
}

Modal.js

import React, { Component } from 'react';
import { Modal, Dimensions, TouchableWithoutFeedback, StyleSheet, View, Text, Button, Image, TouchableOpacity } from 'react-native';

const deviceHeight = Dimensions.get("window").height
export class Popup extends Component {
    constructor(props) {
        super(props)
        this.state = {
            show: false,   
        }
    }

    show = () => {
        this.setState({ show: true })
    }
    close = () => {
        this.setState({ show: false })
    }

    
    renderOutsideTouchable(onTouch) {
        const view = <View style={{ flex: 1, width: '100%' }} />
        if (!onTouch) return view

        return (
            <TouchableWithoutFeedback onPress={onTouch} style={{ flex: 1, width: '100%' }}>
                {view}
            </TouchableWithoutFeedback>
        )
    }

    renderTitle = () => {
        const { name } = this.props
        return (
            <View style={{ alignItems: 'center' }}>
                <Text style={{
                    color: 'black', fontSize: 20,
                    fontWeight: 'bold', margin: 15
                }}>
                    {name}
                </Text>
            </View>
        )
    }
    renderContent = (item) => {
        return (
            <View style={{ alignItems: 'center', marginBottom: 10 }}>
           <View style={styles.card}>
  <Text style={{ fontSize: 20, fontWeight: '500', fontWeight: 'bold', alignSelf: 'center', margin: 5 }}/>
                   
                    <TouchableOpacity style={styles.buttonContainer}>
                        <Text style={styles.button}>Add to Cart</Text>
                    </TouchableOpacity>
                </View>
            </View>
        )
    }

    render() {
        let { show } = this.state
        const { onTouchOutside, title } = this.props
        return (
         <Modal  animationType={'slide'} transparent={true} visible={show} onRequestClose={this.close}>
          <View style={{ flex: 1, backgroundColor: '#000000AA', justifyContent: 'flex-end' }}>
                    {this.renderOutsideTouchable(onTouchOutside)}
           <View style={{
                        backgroundColor: '#FFFFFF', width: '100%', height: '70%', borderTopRightRadius:20, borderTopLeftRadius: 20, paddingHorizontal: 20, maxHeight: deviceHeight * 5}}>

                        {this.renderTitle()}
                        {this.renderContent()}

                    </View>
                </View>
            </Modal>
        )
    }
}

My Problem: I am not able to pass the flatlist item data to a Modal component and have no better idea solving it in this code.

Please help me and if any including, changes or complete solution for perfect understanding for the requirement would be really great. Many Thanks in Advance!

Upvotes: 2

Views: 3860

Answers (1)

Drew Reese
Drew Reese

Reputation: 202605

You don't need to duplicate open state in modal/popup. Simply set the style and open. Assume open close state is controlled by parent component, so if rendering modal/popup it is open by definition.

class Popup extends React.Component {
  renderOutsideTouchable(onTouch) {
    const view = <View style={{ flex: 1, width: '100%' }} />;
    if (!onTouch) return view;

    return (
      <TouchableWithoutFeedback
        onPress={onTouch}
        style={{ flex: 1, width: '100%' }}>
        {view}
      </TouchableWithoutFeedback>
    );
  }

  renderTitle = () => {
    const { name } = this.props;
    return (
      <View style={{ alignItems: 'center' }}>
        <Text
          style={{
            color: 'black',
            fontSize: 20,
            fontWeight: 'bold',
            margin: 15,
          }}>
          {name}
        </Text>
      </View>
    );
  };

  renderContent = () => {
    const { item } = this.props;
    return (
      <View style={{ alignItems: 'center', marginBottom: 10 }}>
        <View style={styles.card}>
          <Text
            style={{
              fontSize: 20,
              fontWeight: '500',
              // fontWeight: "bold",
              alignSelf: 'center',
              margin: 5,
            }}
          />

          <TouchableOpacity style={styles.buttonContainer}>
            <Text>Name: {item.name}</Text>
            <Text>Price: {item.price}</Text>
            <Text>Description: {item.desc}</Text>
            <Text>Rating: {item.rating}</Text>
            <Text style={styles.button}>Add to Cart</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  };

  render() {
    const { onTouchOutside, title } = this.props;
    return (
      <Modal
        animationType={'slide'}
        transparent
        visible // <-- visible prop is truthy
        onRequestClose={this.close}>
        <View
          style={{
            flex: 1,
            backgroundColor: '#000000AA',
            justifyContent: 'flex-end',
            zIndex: 1000,
          }}>
          {this.renderOutsideTouchable(onTouchOutside)}
          <View
            style={{
              backgroundColor: '#FFFFFF',
              width: '100%',
              height: '70%',
              borderTopRightRadius: 20,
              borderTopLeftRadius: 20,
              paddingHorizontal: 20,
              maxHeight: deviceHeight * 5,
            }}>
            {this.renderTitle()}
            {this.renderContent()}
          </View>
        </View>
      </Modal>
    );
  }
}

A react ref isn't necessary for opening a modal/popup to display a specific item. Change the onShowPopup and onClosePopup to set/nullify a clicked on item. Conditionally render the Popup outside the Flatlist.

class Home extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      dataSource: productdata.product,
      isLoading: false,
      popupItem: null,
    };
  }

  onShowPopup = (popupItem) => {
    this.setState({ popupItem });
  };

  onClosePopup = () => {
    this.setState({ popupItem: null });
  };

  ProductList = ({ item }) => (
    <View style={styles.listItem}>
      <TouchableOpacity
        onPress={() => this.onShowPopup(item)}
        style={{
          height: 100,
          width: 100,
          justifyContent: 'center',
          alignItems: 'center',
        }}>
        <Image
          source={item.photo}
          style={{ width: 100, height: 100, borderRadius: 15 }}
        />
      </TouchableOpacity>
      <View style={{ alignItems: 'center', flex: 1, marginTop: 20 }}>
        <Text style={{ fontWeight: 'bold', fontSize: 22 }}>{item.name}</Text>
        <Text style={{ fontSize: 18, fontWeight: 'bold' }}>{item.price}</Text>
      </View>
    </View>
  );

  render() {
    return (
      <View style={styles.container}>
        {this.state.popupItem && (
          <Popup
            name="Product Details"
            item={this.state.popupItem} // <-- pass pop item
            onTouchOutside={this.onClosePopup}
          />
        )}
        <FlatList
          data={this.state.dataSource}
          renderItem={this.ProductList}
          keyExtractor={(item) => item.name}
        />
      </View>
    );
  }
}

Expo Snack

Upvotes: 1

Related Questions