React-modal: Unmount modal on close?

Created on 24 Nov 2015  路  17Comments  路  Source: reactjs/react-modal

Is it possible to unmount the modal when clicking on the close button, instead of changing its state?
I want to initialize the modal with new data every time I click an element on a page, and unmounting it would solve some of the issues I'm having.

Most helpful comment

Hi Guys,
I know this is a very old post, but many people still might encounter this issue like me and I found its really confusing on internet to find a clear solution which will work.
Recently I encountered similar issue when I wanted to clear form fields when a modal closes.
Before(Problem) : pass visible true/false in the modal component to show/hide the modal
After(Solved the problem): mount and unmount the modal based on true/false state value

eg: instead of
<Modal visible={this.state.visible}> blah blah</Modal>

do this
{ (this.state.visible) ? <Modal visible={true}>blah blah</Modal> : null }
This way you can mount and unmount Modal and clear all fields needed in componentWillUnmount()

All 17 comments

I think you can't unmount the modal unless you also unmount its parent component. However, I suggest implementing the initialization you require within an onClick handler bound to the element that opens the modal. And then if you need to do any cleanup upon closing the modal, you can pass it a callback in the onRequestClose prop.

Thank you for your reply. I don't think it's that easy for my current project.
I'll try to explain it a little bit better.

I've got a couple of SVG elements on the page. Each element has data-attributes attached to it.
On click it displays the modal with the relevant content. There is an edit button that changes the modal to have a form where you can change the values. Because of that, I'm keeping the content in the modal's state. That means I have to update the state every time a click happens on an SVG element. On initial load it works (that's why I asked how to unmount the modal), but afterwards I don't really know how to keep passing data to the modal on click, apart from using componentWillReceiveProps. But this method doesn't re-render the modal, so I'll just be one state behind. Hopefully this makes sense.

I tried to strip down the code as much as I could. This is my first React project, so this code might look horrendous:

var ProjectContent = React.createClass({
  getInitialState: function() {
    return {
      show_modal: false,
      modal_data: {}
    };
  },

  componentDidMount: function() {
    // svg is an external element, can't be used as a React Component.
    $(".svg-box").on('click', function(e) {
      _self.setState({
        modal_data: {
          content: content,
        },
        show_modal: true
      });
    });
  },

  render: function() {
    if (!$.isEmptyObject(this.state.modal_data)) {
      project_modal = <ProjectModal
                        modal_data={this.state.modal_data}
                        show_modal={true}
                      />;
    }
    return (
      <div>
        {project_modal}
      </div>
    );
  }
});

var ProjectModal = React.createClass({
  getInitialState: function() {
    return {
      content: this.props.modal_data.content,
      modal_open: this.props.show_modal,
      edit_mode: false
    };
  },

  componentWillReceiveProps: function(nextProps) {
    this.setState({
      modal_open: this.props.show_modal,
      content: this.props.modal_data.content,
    });
  },

  closeModal: function() {
    this.setState({modal_open: false});
    this.setState({edit_mode: false});
  },

  showForm: function() {
    this.setState({edit_mode: true});
  }, 

  saveForm: function() {
    this.setState({edit_mode: false});
    this.updateElementServer();
  },   

  render: function() {
    var modal_content = '';
    if (this.state.edit_mode) {
      modal_content = <div><textarea value={this.state.content} onChange={this.handleContentChange}></textarea></div>;
    } else {
      modal_content = <p>{this.state.content}</p>;
    }

    return (
      <div>
        <Modal
          className="Modal__Bootstrap modal-dialog"
          closeTimeoutMS={150}
          isOpen={this.state.modal_open}
          onRequestClose={this.closeModal}
          style={customStyles} >
          <form>
            <div className="modal-content">
              <div className="modal-body">
                {modal_content}
                <button type="button" onClick={this.showForm}>Edit</button>
                <button type="button" onClick={this.saveForm}>Save</button>
                <button type="button" onClick={this.closeModal}>Close</button>
              </div>
            </div>
          </form>
        </Modal>
      </div>
    );
  }
});

I just had a quick glance, and maybe this is your problem.

Change this

  componentWillReceiveProps: function(nextProps) {
    this.setState({
      modal_open: this.props.show_modal,
      content: this.props.modal_data.content,
    });
  },

to

  componentWillReceiveProps: function(nextProps) {
    this.setState({
      modal_open: this.nextProps.show_modal,
      content: this.nextProps.modal_data.content,
    });
  },

Thank you! This was actually it.
I also had another error: this.nextProps should be nextProps.

I changed the code quite a lot to get to this stage, and I think I got a little bit lost.
I also wasn't sure if using componentWillReceiveProps after getInitialState to change the modal content was the right way of doing things.

This issue can be closed as far as I'm concerned. :bow:

Hi Guys,
I know this is a very old post, but many people still might encounter this issue like me and I found its really confusing on internet to find a clear solution which will work.
Recently I encountered similar issue when I wanted to clear form fields when a modal closes.
Before(Problem) : pass visible true/false in the modal component to show/hide the modal
After(Solved the problem): mount and unmount the modal based on true/false state value

eg: instead of
<Modal visible={this.state.visible}> blah blah</Modal>

do this
{ (this.state.visible) ? <Modal visible={true}>blah blah</Modal> : null }
This way you can mount and unmount Modal and clear all fields needed in componentWillUnmount()

Hi @priyanka-jalan , wouldn't your solution makes the modal disappear abruptly without showing the fade-in animation?

Yes, the above method works but then you lose the fade-out transition effect on close.

@priyanka-jalan doesn't work for me

For those who are having a similar issue, there's a simple solution:

Instead of setting the state with a object, create a function that returns the initial state. Then you check the modal props, if the the previous prop was closed and the new is open, you know that the modal was opened. Then you can just call the initial state function and use the value to set the state, basically "reseting" the state.

@kvnsnchz This solution is just a hack. What if my animation is greater than 500 ms?

In my opinion - modal should be mounted only when opened. If the modal is mounted even when not rendered/opened - the developer is restricted to perform things like fetching of some data etc. outside the modal. Would love if the maintainers can share their thoughts on why do they keep the modal mounted even if it is not in use.

Using conditional render on newest versions of react will cause the portal (its DOM tree) to be unconditionally destroyed (this will cause the fade out animation to not work). So I'd recommend, until we find a proper solution, to not use conditional render. I believe this has changed on react 16.3+.

For any other question, please open a new issue with a minimal reproducible example (if possible), on codepen or codesandbox. I'm currently moving to another city, so mention me on the new issue and I'll do my best to give some help on this week.

Thanks to all of you for reporting all these issues.

I tried it and it works:
https://codepen.io/ytxmobile/pen/pXgddN

Hi Guys,
I know this is a very old post, but many people still might encounter this issue like me and I found its really confusing on internet to find a clear solution which will work.
Recently I encountered similar issue when I wanted to clear form fields when a modal closes.
Before(Problem) : pass visible true/false in the modal component to show/hide the modal
After(Solved the problem): mount and unmount the modal based on true/false state value

eg: instead of
<Modal visible={this.state.visible}> blah blah</Modal>

do this
{ (this.state.visible) ? <Modal visible={true}>blah blah</Modal> : null }
This way you can mount and unmount Modal and clear all fields needed in componentWillUnmount()

For those still facing this issue, I have found a neat solution that retains the closing animation of the Modal using hooks.

Inside the component that contains the Modal, create a key constant with useState(), set initially to 0:

const [key, setKey] = React.useState(0)

Then inside the Modal, wrap your content in a div setting the key attribute to your key:

<Modal visible={visible} onClose={handleClose}>
    <div key={key}>
        Content you want to re-mount when opening modal
    </div>
</Modal>

Then inside your handleClose method simply increment your key:

const handleClose = () => {
    setKey(key + 1)
    setVisible(false)
}

The way this works is that by incrementing the key, React treats the div as though its a never-before-seen component, and will mount it each time. By incrementing inside onClose it ensures that when you next open it, the key will be different than the previous mount.

Why not just reset the state on close function of modal and get the new data when open function of modal is clicked?

In my case, I was using const [searchFilters, setSearchFilters] = useState(dataFromRedux) in Modal,
and in parent component, I can clear current filters, which makes the redux data update, but the Modal state is not updated. But I don't want to always reset all the states of my Modal if I don't clear filters. That's why for me.

I have to lift my state up in the parent and manually setState if I clear the filters. But I think resetting can also do the trick for me as well, the same as for https://github.com/reactjs/react-modal/issues/106#issuecomment-546658885 . And I think the answer from https://github.com/reactjs/react-modal/issues/106#issuecomment-546658885 is the same idea to reset the state. When the key changed, the modal component is remounted.

Why not just reset the state on close function of modal and get the new data when open function of modal is clicked?

Was this page helpful?
0 / 5 - 0 ratings