How to use multiple dialog boxes and render modals in ReactJS

10,865

You have a great start, but you're missing a few items. You just need to make your components more flexible and reusable (see below for comments).

Some notes: The example below uses ES6 destructuring, ES6 Fat Arrow Functions, the map function, the spread operator, and callback functions. In addition, you can't wrap a clickable element (Modal) inside of another clickable element (div). The outer most element (div) will only be handled by the onClick event handler.

Working example (for simplicity, I'm not using images, instead I'm using placeholder example.png titles that are clickable):

Edit Reusable DialogBox


components/Modal

import React from "react";
import PropTypes from "prop-types";
import {
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle
} from "@material-ui/core";


// "Modal is a pure function that requires 4 parameters in order to display 
// the "<Modal />" component. This function would be the same as a traditional function:
// 
//   function Modal({ deconstructed parameters }) { 
//     return (
//       <Dialog>
//        ...
//      </Dialog>
//     )
//   }

const Modal = ({ description, onCloseModal, openModal, title }) => ( 
  <Dialog
    open={openModal}
    onClose={onCloseModal}
    aria-labelledby="alert-dialog-title"
    aria-describedby="alert-dialog-description"
  >
    <DialogTitle id="alert-dialog-title">{title}</DialogTitle>
    <DialogContent>
      <DialogContentText id="alert-dialog-description">
        {description}
      </DialogContentText>
    </DialogContent>
    <DialogActions>
      <Button onClick={onCloseModal} color="primary">
        Okay
      </Button>
    </DialogActions>
  </Dialog>
);

// PropTypes throws a warning if any of the 4 required params are
// missing! In addition, it ensures that props are consistent in name
// and declaration from parent component to child component.

Modal.propTypes = {
  description: PropTypes.string.isRequired,
  onCloseModal: PropTypes.func.isRequired,
  openModal: PropTypes.bool.isRequired,
  title: PropTypes.string.isRequired
};

export default Modal;

components/Scenario

import React, { PureComponent } from "react";
import PropTypes from "prop-types";


// Scenario will be a PureComponent (stateless, but still a class component) 
// that will open the Modal component with a supplied "description" and 
// "title" via a parent callback function named "handleOpenModal"

class Scenario extends PureComponent {
  openModal = () => {
    const { description, handleOpenModal, title } = this.props;
    handleOpenModal({ description, title });
  };

  render = () => (
    <div className="scenario" onClick={this.openModal}>
      <h1>{this.props.imgSrc}</h1>
    </div>
  );
}

Scenario.propTypes = {
  description: PropTypes.string.isRequired,
  handleOpenModal: PropTypes.func.isRequired,
  imgSrc: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired
};

export default Scenario;

components/Scenarios

import React, { Component } from "react";
import Modal from "../Modal";
import Scenario from "../Scenario/";

// A scenarios variable of an array of objects -- makes it so we don't have to 
// repeat <Scenario title=".." description="..."imgSrc={...} handleOpenModal={..} /> 
// over and over, instead we map over these objects as "props" and spread
// them out inside of "Scenario" like so: <Scenario {...props} />, which 
// would be the same as:  
// <Scenario title={title} description={description} imgSrc={imgSrc} />

const scenarios = [
  {
    title: "Car",
    description: "This is a description for a car.",
    imgSrc: "car.png"
  },
  {
    title: "Home",
    description: "This is a description for a home.",
    imgSrc: "home.png"
  },
  {
    title: "Bed",
    description: "This is a description for a bed.",
    imgSrc: "bed.png"
  },
  {
    title: "Pet",
    description: "This is a description for a pet.",
    imgSrc: "pet.png"
  }
];

// Scenarios is a stateful class component that'll act as the parent 
// for its "Scenario" children. The children will update the parent via 
// "this.handleOpenModal". Meanwhile, "Modal" will sit inside the parent
// waiting for the parent state updates to affect how its rendered. The 
// Modal will close itself via the parent's "this.handleCloseModal"
// class method when the "Okay" button is clicked.

class Scenarios extends Component {
  state = { description: "", openModal: false, title: "" };

  handleOpenModal = ({ description, title }) => {
    this.setState({ description, openModal: true, title });
  };

  handleCloseModal = () => {
    this.setState({ openModal: false });
  };

  render = () => (
    <section className="App-scenarios">
      <h2> Quick Tips </h2>
      <p>Know What to Do in Different Scenarios during an Earthquake</p>
      <div className="scenario-group">
        {scenarios.map(props => (
          <Scenario
            {...props}
            key={props.title} // this is required for React to know that each "Scenario" that is returned by the "map" function is unique and must be handled as individual components (otherwise React will complain and throw a warning)
            handleOpenModal={this.handleOpenModal}
          />
        ))}
      </div>
      <Modal {...this.state} onCloseModal={this.handleCloseModal} />
    </section>
  );
}

export default Scenarios;
Share:
10,865
Admin
Author by

Admin

Updated on June 05, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm having trouble figuring out how to use Material UI's dialog box to render modals, close them upon button clicking and also make it so that clicking on different things brings up different modals.

    This is the dialog component that I took from Material UI

    import React from 'react';
    import Button from '@material-ui/core/Button';
    import Dialog from '@material-ui/core/Dialog';
    import DialogActions from '@material-ui/core/DialogActions';
    import DialogContent from '@material-ui/core/DialogContent';
    import DialogContentText from '@material-ui/core/DialogContentText';
    import DialogTitle from '@material-ui/core/DialogTitle';
    
    
    class DialogBox extends React.Component {
    
      render() {
        return (
          <Dialog
          open={this.props.open}
          onClose={this.props.handleClose}
          aria-labelledby="alert-dialog-title"
          aria-describedby="alert-dialog-description"
        >
          <DialogTitle id="alert-dialog-title">{"Use Google's location service?"}</DialogTitle>
          <DialogContent>
            <DialogContentText id="alert-dialog-description">
              Let Google help apps determine location. This means sending anonymous location data to
              Google, even when no apps are running.
            </DialogContentText>
          </DialogContent>
          <DialogActions>
            <Button onClick={this.props.handleClose} color="primary">
              Okay
            </Button>
          </DialogActions>
        </Dialog>
        );
      }
    }
    
    
    export default DialogBox;

    This is the page that I am rendering the Dialog Box in, I called it Modal in here. How do I make it so that I can close it when the dialog is open and also so I can click on a different picture and have it open up a dialog with different text?

    import React,{Component} from "react";
    import home from "./home.png";
    import car from "./car.png";
    import bed from "./bed.png";
    import pet from "./pet.png";
    import Dialog from "./Modal.js";
    
    class Scenarios extends Component {
      constructor(){
        super();
        this.state = { open: false };
      }
    
      openModal = () =>{
        this.setState({ open: true });
      }
    
      handleClose = () => {
        this.setState({ open: false });
      };
    
      render() {
        return (
           <section className="App-scenarios">
            <h2> Quick Tips </h2>
            <p>Know What to Do in Different Scenarios during an Earthquake</p>
            <div className="scenario-group">
    
              <div className="scenario" onClick={this.openModal}>
                <img src={car}/>
              </div> 
    
              <div className="scenario" onClick={this.openModal}>
              <img src={home}/>
                <Dialog open={this.state.open} onClose={this.handleClose} title="Home" description="text" />
              </div>
              <div className="scenario" >
              <img src={bed}/>
              </div>
              
    
              <div className="scenario">
              <img src={pet}/>
              </div>
            </div>
          </section>
        );
        }
    };
    
    export default Scenarios;