Bootstrap modal in React.js

JavascriptTwitter BootstrapCrudReactjs

Javascript Problem Overview


I need to open a Bootstrap Modal from clicking on a button in a Bootstrap navbar and other places (to show data for a component instance, ie. providing "editing" functionality), but I don't know how to accomplish this. Here is my code:

EDIT: Code updated.

ApplicationContainer = React.createClass({
    render: function() {
        return (
            <div className="container-fluid">
            <NavBar />
                <div className="row">
                    <div className="col-md-2">
                        <ScheduleEntryList />
                    </div>
                    <div className="col-md-10">
                    </div>
                </div>
                <ScheduleEntryModal />
            </div>
        );
    }
});

NavBar = React.createClass({
    render: function() {
        return (
            <nav className="navbar navbar-default navbar-fixed-top">
                <div className="container-fluid">
                    <div className="navbar-header">
                        <a className="navbar-brand" href="#">
                            <span className="glyphicon glyphicon-eye-open"></span>
                        </a>
                    </div>
                    <form className="navbar-form navbar-left">
                        <button className="btn btn-primary" type="button" data-toggle="modal" data-target="#scheduleentry-modal">
                            <span className="glyphicon glyphicon-plus">
                            </span>
                        </button>
                    </form>
                    <ul className="nav navbar-nav navbar-right">
                        <li><a href="#"><span className="glyphicon glyphicon-user"></span> Username</a></li>
                    </ul>
                </div>
            </nav>
        );
    }
});

ScheduleEntryList = React.createClass({
    getInitialState: function() {
        return {data: []}
    },

    loadData: function() {
        $.ajax({
            url: "/api/tasks",
            dataType: "json",

            success: function(data) {
                this.setState({data: data});
            }.bind(this),

            error: function(xhr, status, error) {
                console.error("/api/tasks", status, error.toString());
            }.bind(this)
        });
    },

    componentWillMount: function() {
        this.loadData();
        setInterval(this.loadData, 20000);
    },

    render: function() {
        items = this.state.data.map(function(item) {
            return <ScheduleEntryListItem item={item}></ScheduleEntryListItem>;
        });

        return (
            <div className="list-group">
                <a className="list-group-item active">
                    <h5 className="list-group-item-heading">Upcoming</h5>
                </a>
                {items}
            </div>
        );
    }
});

ScheduleEntryListItem = React.createClass({
    openModal: function() {
        $("#scheduleentry-modal").modal("show");
    },

    render: function() {
        deadline = moment(this.props.item.deadline).format("MMM Do YYYY, h:mm A");

        return (
            <a className="list-group-item" href="#" onClick={this.openModal}>
                <h5 className="list-group-item-heading">
                    {this.props.item.title}
                </h5>
                <small className="list-group-item-text">
                    {deadline}
                </small>
            </a>
        );
    }
});

Modal = React.createClass({
    componentDidMount: function() {
        $(this.getDOMNode())
            .modal({backdrop: "static", keyboard: true, show: false});
    },

    componentWillUnmount: function() {
        $(this.getDOMNode())
            .off("hidden", this.handleHidden);
    },

    open: function() {
        $(this.getDOMNode()).modal("show");
    },

    close: function() {
        $(this.getDOMNode()).modal("hide");
    },

    render: function() {
        return (
            <div id="scheduleentry-modal" className="modal fade" tabIndex="-1">
                <div className="modal-dialog">
                    <div className="modal-content">
                        <div className="modal-header">
                            <button type="button" className="close" data-dismiss="modal">
                                <span>&times;</span>
                            </button>
                            <h4 className="modal-title">{this.props.title}</h4>
                        </div>
                        <div className="modal-body">
                            {this.props.children}
                        </div>
                        <div className="modal-footer">
                            <button type="button" className="btn btn-danger pull-left" data-dismiss="modal">Delete</button>
                            <button type="button" className="btn btn-primary">Save</button>
                        </div>
                    </div>
                </div>
            </div>

        )
    }
});

ScheduleEntryModal = React.createClass({
    render: function() {
        var modal = null;
        modal = (
            <Modal title="Add Schedule Entry">
                    <form className="form-horizontal">
                        <div className="form-group">
                            <label htmlFor="title" className="col-sm-2 control-label">Title</label>
                            <div className="col-sm-10">
                                <input id="title" className="form-control" type="text" placeholder="Title" ref="title" name="title"/>
                            </div>
                        </div>
                        <div className="form-group">
                            <label htmlFor="deadline" className="col-sm-2 control-label">Deadline</label>
                            <div className="col-sm-10">
                                <input id="deadline" className="form-control" type="datetime-local" ref="deadline" name="deadline"/>
                            </div>
                        </div>
                        <div className="form-group">
                            <label htmlFor="completed" className="col-sm-2 control-label">Completed</label>
                            <div className="col-sm-10">
                                <input id="completed" className="form-control" type="checkbox" placeholder="completed" ref="completed" name="completed"/>
                            </div>
                        </div>
                        <div className="form-group">
                            <label htmlFor="description" className="col-sm-2 control-label">Description</label>
                            <div className="col-sm-10">
                                <textarea id="description" className="form-control" placeholder="Description" ref="description" name="description"/>
                            </div>
                        </div>
                    </form>
            </Modal>
        );

        return (
            <div className="scheduleentry-modal">
                {modal}
            </div>
        );
    }
});

Other comments and improvements to the code are appreciated.

Javascript Solutions


Solution 1 - Javascript

I was recently looking for a nice solution to this without adding React-Bootstrap to my project (as Bootstrap 4 is about to be released).

This is my solution: https://jsfiddle.net/16j1se1q/1/

let Modal = React.createClass({
    componentDidMount(){
        $(this.getDOMNode()).modal('show');
        $(this.getDOMNode()).on('hidden.bs.modal', this.props.handleHideModal);
    },
    render(){
    	return (
          <div className="modal fade">
            <div className="modal-dialog">
              <div className="modal-content">
                <div className="modal-header">
                  <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                  <h4 className="modal-title">Modal title</h4>
                </div>
                <div className="modal-body">
                  <p>One fine body&hellip;</p>
                </div>
                <div className="modal-footer">
                  <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
                  <button type="button" className="btn btn-primary">Save changes</button>
                </div>
              </div>
            </div>
          </div>
        )
    },
    propTypes:{
    	handleHideModal: React.PropTypes.func.isRequired
    }
});



let App = React.createClass({
    getInitialState(){
      	return {view: {showModal: false}}
    },
    handleHideModal(){
    	this.setState({view: {showModal: false}})
    },
    handleShowModal(){
    	this.setState({view: {showModal: true}})
    },
    render(){
    return(
    	<div className="row">
    		<button className="btn btn-default btn-block" onClick={this.handleShowModal}>Open Modal</button>
        	{this.state.view.showModal ? <Modal handleHideModal={this.handleHideModal}/> : null}
    	</div>
    );
  }
});

React.render(
   <App />,
    document.getElementById('container')
);

The main idea is to only render the Modal component into the React DOM when it is to be shown (in the App components render function). I keep some 'view' state that indicates whether the Modal is currently shown or not.

The 'componentDidMount' and 'componentWillUnmount' callbacks either hide or show the modal (once it is rendered into the React DOM) via Bootstrap javascript functions.

I think this solution nicely follows the React ethos but suggestions are welcome!

Solution 2 - Javascript

You can use React-Bootstrap (https://react-bootstrap.github.io/components/modal). There is an example for modals at that link. Once you have loaded react-bootstrap, the modal component can be used as a react component:

var Modal = ReactBootstrap.Modal;

can then be used as a react component as <Modal/>.

For Bootstrap 4, there is react-strap (https://reactstrap.github.io). React-Bootstrap only supports Bootstrap 3.

Solution 3 - Javascript

getDOMNode() is deprecated. Instead use ref to access DOM element. Here is working Modal component (Bootstrap 4). Decide whether to show or not to show Modal component in parent component.

Example: https://jsfiddle.net/sqfhkdcy/

class Modal extends Component {
    constructor(props) {
        super(props);
    }
    componentDidMount() {
        $(this.modal).modal('show');
        $(this.modal).on('hidden.bs.modal', handleModalCloseClick);
    }
    render() {
        return (
            <div>
                <div className="modal fade" ref={modal=> this.modal = modal} id="exampleModal" tabIndex="-1" role="dialog" aria- labelledby="exampleModalLabel" aria-hidden="true">
                    <div className="modal-dialog" role="document">
                        <div className="modal-content">
                            <div className="modal-header">
                                <h5 className="modal-title" id="exampleModalLabel">Modal title
                                </h5>
                                <button type="button" className="close" data- dismiss="modal" aria-label="Close">
                                    <span aria-hidden="true">&times;</span>
                                </button>
                            </div>
                            <div className="modal-body">
                                ...
                            </div>
                            <div className="modal-footer">
                                <button type="button" className="btn btn-secondary" data- dismiss="modal">Close</button>
                                <button type="button" className="btn btn-primary">Save changes</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

Edit:

Here are the necessary imports to make it work:

import $ from 'jquery';
window.jQuery = $;
window.$ = $;
global.jQuery = $;

Solution 4 - Javascript

I've only used bootstrap cdn (css + js) to achieve "reactstrap" like solution. I've used props.children to pass dynamic data from parent to child components. You can find more about this here. In this way you have three separate components modal header, modal body and modal footer and they are totally independent from each other.


//Modal component
import React, { Component } from 'react';

export const ModalHeader = props => {
  return <div className="modal-header">{props.children}</div>;
};

export const ModalBody = props => {
  return <div className="modal-body">{props.children}</div>;
};

export const ModalFooter = props => {
  return <div className="modal-footer">{props.children}</div>;
};

class Modal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      modalShow: '',
      display: 'none'
    };
    this.openModal = this.openModal.bind(this);
    this.closeModal = this.closeModal.bind(this);
  }

  openModal() {
    this.setState({
      modalShow: 'show',
      display: 'block'
    });
  }

  closeModal() {
    this.setState({
      modalShow: '',
      display: 'none'
    });
  }

  componentDidMount() {
    this.props.isOpen ? this.openModal() : this.closeModal();
  }

  componentDidUpdate(prevProps) {
    if (prevProps.isOpen !== this.props.isOpen) {
      this.props.isOpen ? this.openModal() : this.closeModal();
    }
  }

  render() {
    return (
      <div
        className={'modal fade ' + this.state.modalShow}
        tabIndex="-1"
        role="dialog"
        aria-hidden="true"
        style={{ display: this.state.display }}
      >
        <div className="modal-dialog" role="document">
          <div className="modal-content">{this.props.children}</div>
        </div>
      </div>
    );
  }
}

export default Modal;

//App component
import React, { Component } from 'react';
import Modal, { ModalHeader, ModalBody, ModalFooter } from './components/Modal';

import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      modal: false
    };
    this.toggle = this.toggle.bind(this);
  }

  toggle() {
    this.setState({ modal: !this.state.modal });
  }

  render() {
    return (
      <div className="App">
        <h1>Bootstrap Components</h1>

        <button
          type="button"
          className="btn btn-secondary"
          onClick={this.toggle}
        >
          Modal
        </button>

        <Modal isOpen={this.state.modal}>
          <ModalHeader>
            <h3>This is modal header</h3>
            <button
              type="button"
              className="close"
              aria-label="Close"
              onClick={this.toggle}
            >
              <span aria-hidden="true">&times;</span>
            </button>
          </ModalHeader>
          <ModalBody>
            <p>This is modal body</p>
          </ModalBody>
          <ModalFooter>
            <button
              type="button"
              className="btn btn-secondary"
              onClick={this.toggle}
            >
              Close
            </button>
            <button
              type="button"
              className="btn btn-primary"
              onClick={this.toggle}
            >
              Save changes
            </button>
          </ModalFooter>
        </Modal>
      </div>
    );
  }
}

export default App;
    

Solution 5 - Javascript

I Created this function:

onAddListItem: function () {
    var Modal = ReactBootstrap.Modal;
    React.render((
        <Modal title='Modal title' onRequestHide={this.hideListItem}>
            <ul class="list-group">
                <li class="list-group-item">Cras justo odio</li>
                <li class="list-group-item">Dapibus ac facilisis in</li>
                <li class="list-group-item">Morbi leo risus</li>
                <li class="list-group-item">Porta ac consectetur ac</li>
                <li class="list-group-item">Vestibulum at eros</li>
            </ul>
        </Modal>
    ), document.querySelector('#modal-wrapper'));
}

And then used it on my Button trigger.

To 'hide' the Modal:

hideListItem: function () {
    React.unmountComponentAtNode(document.querySelector('#modal-wrapper'));
},

Solution 6 - Javascript

You can use the model from the react-bootstrap from link and it's basically a function based

function Example() {
  const [show, setShow] = useState(false);
  const handleClose = () => setShow(false);
  const handleShow = () => setShow(true);
  return (
    <>
      <Button variant="primary" onClick={handleShow}>
        Launch demo modal
      </Button>

      <Modal show={show} onHide={handleClose} animation={false}>
        <Modal.Header closeButton>
          <Modal.Title>Modal heading</Modal.Title>
        </Modal.Header>
        <Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            Close
          </Button>
          <Button variant="primary" onClick={handleClose}>
            Save Changes
          </Button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

and You can convert it into the class component

import React, { Component } from "react";
import { Button, Modal } from "react-bootstrap";


export default class exampleextends Component {
  constructor(props) {
    super(props);
    this.state = {
      show: false,
      close: false,
    };
  } 
  render() {
    return (
      <div>
        <Button
          variant="none"
          onClick={() => this.setState({ show: true })}
        >
          Choose Profile
        </Button>
        <Modal
          show={this.state.show}
          animation={true}
          size="md" className="" shadow-lg border">
          <Modal.Header className="bg-danger text-white text-center py-1">
            <Modal.Title className="text-center">
              <h5>Delete</h5>
            </Modal.Title>
          </Modal.Header>
          <Modal.Body className="py-0 border">
            body   
          </Modal.Body>
<Modal.Footer className="py-1 d-flex justify-content-center">
              <div>
                <Button
                  variant="outline-dark" onClick={() => this.setState({ show: false })}>Cancel</Button>
              </div>
              <div>
                <Button variant="outline-danger" className="mx-2 px-3">Delete</Button>
              </div>
            </Modal.Footer>
        </Modal>
      </div>
    );
  }
}

Solution 7 - Javascript

You can try this modal:https://github.com/xue2han/react-dynamic-modal It is stateless and can be rendered only when needed.So it is very easy to use.Just like this:

    class MyModal extends Component{
       render(){
          const { text } = this.props;
          return (
             <Modal
                onRequestClose={this.props.onRequestClose}
                openTimeoutMS={150}
                closeTimeoutMS={150}
                style={customStyle}>
                <h1>What you input : {text}</h1>
                <button onClick={ModalManager.close}>Close Modal</button>
             </Modal>
          );
       }
    }

    class App extends Component{
        openModal(){
           const text = this.refs.input.value;
           ModalManager.open(<MyModal text={text} onRequestClose={() => true}/>);
        }
        render(){
           return (
              <div>
                <div><input type="text" placeholder="input something" ref="input" /></div>
                <div><button type="button" onClick={this.openModal.bind(this)}>Open Modal </button> </div>
              </div>
           );
        }
    }

    ReactDOM.render(<App />,document.getElementById('main'));

Solution 8 - Javascript

Thanks to @tgrrr for a simple solution, especially when 3rd party library is not wanted (such as React-Bootstrap). However, this solution has a problem: modal container is embedded inside react component, which leads to modal-under-background issue when outside react component (or its parent element) has position style as fixed/relative/absolute. I met this problem and came up to a new solution:

"use strict";

var React = require('react');
var ReactDOM = require('react-dom');

var SampleModal = React.createClass({
  render: function() {
    return (
      <div className="modal fade" tabindex="-1" role="dialog">
        <div className="modal-dialog">
          <div className="modal-content">
            <div className="modal-header">
              <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
              <h4 className="modal-title">Title</h4>
            </div>
            <div className="modal-body">
              <p>Modal content</p>
            </div>
            <div className="modal-footer">
              <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
              <button type="button" className="btn btn-primary">OK</button>
            </div>
          </div>
        </div>
      </div>
    );
  }
});

var sampleModalId = 'sample-modal-container';
var SampleApp = React.createClass({
  handleShowSampleModal: function() {
    var modal = React.cloneElement(<SampleModal></SampleModal>);
    var modalContainer = document.createElement('div');
    modalContainer.id = sampleModalId;
    document.body.appendChild(modalContainer);
    ReactDOM.render(modal, modalContainer, function() {
      var modalObj = $('#'+sampleModalId+'>.modal');
      modalObj.modal('show');
      modalObj.on('hidden.bs.modal', this.handleHideSampleModal);
    }.bind(this));
  },
  handleHideSampleModal: function() {
    $('#'+sampleModalId).remove();
  },
  render: function(){    
    return (
      <div>
        <a href="javascript:;" onClick={this.handleShowSampleModal}>show modal</a>
      </div>
    )
  }
});

module.exports = SampleApp;

The main idea is:

  1. Clone the modal element (ReactElement object).
  2. Create a div element and insert it into document body.
  3. Render the cloned modal element in the newly inserted div element.
  4. When the render is finished, show modal. Also, attach an event listener, so that when modal is hidden, the newly inserted div element will be removed.

Solution 9 - Javascript

Reactstrap also has an implementation of Bootstrap Modals in React. This library targets Bootstrap version 4, whereas react-bootstrap targets version 3.X.

Solution 10 - Javascript

The quickest fix would be to explicitly use the jQuery $ from the global context (which has been extended with your $.modal() because you referenced that in your script tag when you did

Solution 11 - Javascript

Just add href='#scheduleentry-modal' to the element you want to open the modal with

Or using jQuery: $('#scheduleentry-modal').modal('show');

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2787959View Question on Stackoverflow
Solution 1 - JavascripttgrrrView Answer on Stackoverflow
Solution 2 - JavascriptMarkView Answer on Stackoverflow
Solution 3 - JavascriptRinat RezyapovView Answer on Stackoverflow
Solution 4 - JavascriptviktorlabView Answer on Stackoverflow
Solution 5 - JavascriptsangressView Answer on Stackoverflow
Solution 6 - JavascriptArshadView Answer on Stackoverflow
Solution 7 - JavascriptxuehanView Answer on Stackoverflow
Solution 8 - JavascriptshaochuancsView Answer on Stackoverflow
Solution 9 - JavascriptcurranView Answer on Stackoverflow
Solution 10 - Javascriptb0nbonView Answer on Stackoverflow
Solution 11 - JavascriptRodrigo De LunaView Answer on Stackoverflow