React-modal: Multiple Modals

Created on 1 May 2016  路  18Comments  路  Source: reactjs/react-modal

When working on my current project, I discovered that it doesn't seem like react-modal appreciates the use of multiple <Modal /> components in one app.

I googled around and then realized that I should use one <Modal /> component and then determine through state/whatever what to display inside the modal.

I really think this should be documented, or at least mentioned in some way in the README to avoid confusion.

Most helpful comment

@czbaker @bf You can make a component to a wrapped modal class. Something like this...

class MyModal {
  render() {
    return (<Modal ...>{this.props.children}</Modal>);
  }
}

All 18 comments

@czbaker you can always control when to actually render the Modal in its container/parent's state so that it doesn't get rendered at all

I also have the issue that modal is used deep down in some business logic, and I was a bit shocked that I ended up with 50 modal instances in my app. Is there any example how to wrap modal in it's own class or could we make it re-use the already existing HTML elements?

@czbaker @bf You can make a component to a wrapped modal class. Something like this...

class MyModal {
  render() {
    return (<Modal ...>{this.props.children}</Modal>);
  }
}
...
  render() {
     return (<MyModal><TheComponent /></MyModal>);
  }
...

or without the wrapper...

...
  render() {
     return (<Modal><TheComponent /></Modal>);
  }
...

@diasbruno Hi can show how to solve this problem

I have many items on a page. I want when I click an item, a modal will show up, and that modal contains specific detail of that item

export default class Grid extends React.Component {
    render() {
        let data = [ 
            {id:1, name: 'john', star: 4}, 
            {id:2, name: 'peter', star: 1}, 
            {id:3, name: 'sasa', star: 3} 
        ]
        return (
            <div>
                {
                    data.map( function(i, index){
                        return (
                            <div className="item" onclick={showmodal()}>
                                <span>{i.id}</span>
                                <span>{i.name}</span>
                                <span>{i.star}</span>
                            </div>
                        )
                    })  
                }
                <Modal
                    overlayClassName="detail-modal-overlay"
                    className="detail-modal"
                    isOpen={this.state.detailModal}
                    onRequestClose={this.closeDetailModal.bind(this)}>

                </Modal>
            </div>
        )
    }
}

modal content should be like this when opened

<div className="modal-content">
    <span>{i.id}</span>
    <span>{i.name}</span>
    <span>{i.star}</span>
</div>

@craigcosmo inside the modal add this {props.children}.

<Modal ...>
  {props.children}
</Modal>

...in your app...

<Grid ...>
  {data.map(function(i, index) {
    <div className="modal-content">
      <span>{i.id}</span>
      <span>{i.name}</span>
      <span>{i.star}</span>
    </div>
  })}
</Grid>

How do you connect the content with the modal?

When you add props.children to a component, whatever goes in between the component tag is rendered inside of it. Probably there are better ways to make the component you want.

class Grid extends React.Component {
  render() {
    // if you use like this, Grid will act like a proxy.
    return (
      <Modal ...>{this.props.children}</Modal>
    }

    // this is a more specific component.
    return {
       <Modal ...>
         {this.prop.data.map(function(item, index) {
           <div className="modal-content">
             <span>{item.id}</span>
             <span>{item.name}</span>
             <span>{item.star}</span>
           </div>
         })}
       </Modal>
     }
  }
}
let data = [ 
  {id:1, name: 'john', star: 4}, 
  {id:2, name: 'peter', star: 1}, 
  {id:3, name: 'sasa', star: 3} 
];

// using props.children
<Grid ...>
  {data.map(function(i, index) {
    <div className="modal-content">
      <span>{i.id}</span>
      <span>{i.name}</span>
      <span>{i.star}</span>
    </div>
  })}
</Grid>
let data = [ 
  {id:1, name: 'john', star: 4}, 
  {id:2, name: 'peter', star: 1}, 
  {id:3, name: 'sasa', star: 3} 
];
<Grid data={data} />

The ... are other properties you may want to pass to component.

Hope it make sense.

ah I think you misunderstood me. Grid is a container which has many items in it. what I want is when I click one of the item, a modal will pop up, the content of the pop up modal is the content of the clicked item.

hmmmmm, you can bind showmodal() with the current id of the rendered item, when you click on it, you can keep the index in the state of the component (currentItem).

<Modal ...>
   ....render data[this.state.currentItem]...
</Modal>

Hope I got it right this time. :)

What understand from your code, you put the <Grid> in <Modal> that will make the whole grid show up in the when it pops up. I just want one item of the grid show up in the

@craigcosmo for example:

ListStaff.js:

import React, { Component, PropTypes } from 'react';
import Paper from 'material-ui/Paper';
import DialogueBox from './DialogueBox';


class ListStaff extends Component {

  constructor(props){

    super(props);

    this.renderRecipeList = this.renderRecipeList.bind(this);
  }

  renderRecipeList(man, index){

    const currentMan = man;
    const styleForMan = {
      margin: 10,
      padding: 10,
      textAlign: "center"
    };

    return(
      <div key={currentMan.id}>
        <DialogueBox currentMan={currentMan}/>
      </div>
    );
  }

  render(){

    const self = this;
    const { staff } = this.props;

    return(
      <div>
        <div>
          {staff.sort((a, b) => a.firstName.toLowerCase() > b.firstName.toLowerCase()).map(self.renderRecipeList)}
        </div>
      </div>
    );
  }
}

ListStaff.propTypes = {
  staff: PropTypes.array.isRequired
};

export default ListStaff;

DialogueBox.js:

import React, { Component, PropTypes } from 'react';
import Dialog from 'material-ui/Dialog';
import Paper from 'material-ui/Paper';


class DialogueBox extends Component {

  constructor(props){

    super(props);

    this.state = {
      showModal: false
    };

    this.toggleModal = this.toggleModal.bind(this);
  }

  toggleModal(){
    this.setState({
      showModal: !this.state.showModal
    });
  }

  render() {
    const styleForMan = {
      margin: 10,
      padding: 10,
      textAlign: "center"
    };

    return (
      <div>
        <Paper style={styleForMan} onClick={this.toggleModal}>
            {this.props.currentMan.firstName}{' '}
            {this.props.currentMan.patronymic}{' '}
            {this.props.currentMan.lastName}
        </Paper>

        <Dialog 
          open={this.state.showModal}
          modal={false}
          onRequestClose={this.toggleModal}>
          {this.props.currentMan.firstName}
          <Paper onClick={this.toggleModal}>Close</Paper>
        </Dialog>
      </div>
    );
  }
}

DialogueBox.propTypes = {
  currentMan: PropTypes.object.isRequired
};

export default DialogueBox;

react-modal can handle multiple modals now. If there is still an issue just reopen.

Wow, encountered this issue or so I thought - it just turned out my second modal's isOpen state variable had an extra character. Multiple modals work just fine, great jobs folks

@craigcosmo - Hey could you lease tell me how did you solve the issue of when I click an item, a modal will show up, and that modal contains specific detail of that item. Currently i am working on it. Please share your answer.

@samateja sure, this is home.js

export default class Home extends React.Component {
    constructor(props) {
        this.state = {item : null}
    }
    passItemToModal(item){
        this.setState({
            detailModal: true,
            item: item
        })
    }
    closeDetailModal() {
        this.setState({
            detailModal: false
        })
    }
    openDetailModal() {
        this.setState({
            detailModal: true
        })
    }
    render(){
        return 
        <Grid passItemToModal={this.passItemToModal.bind(this)}></Grid>
        <Modal
            overlayClassName="detail-modal-overlay"
            className="detail-modal"
            isOpen={this.state.detailModal}
            onRequestClose={this.closeDetailModal.bind(this)}>
            {  this.state.item ? this.renderContent(this.state.item) : null }
        </Modal>
    }
}

So in your gridcomponent, everytime an item clicked. You call the function props passItemToModal(item). That's all

hey, guys you can use react-drag-drawer i found it the best way

Was this page helpful?
0 / 5 - 0 ratings