SaltyCrane Blog — Notes on JavaScript and web development

A chained modals example w/ React Bootstrap

This is an example using React to create a sequence of chained modals. Each modal has a "Next" button that, when clicked, advances to the next modal. The list of modals is configurable at page load. I used React-Bootstrap for the modals which provide animation when switching modals. The full code is here and a demo is here.

App.js

The top level app is a function that returns an element tree consisting of two elements: PageBehindModals and ChainedModals. ChainedModals is a parent of the modals which are passed in via the modalList prop. This uses ES'15 arrow functions. [App.js on github]

const App = () => (
  <div>
    <PageBehindModals />
    <ChainedModals modalList={[ModalName, ModalPhone]} />
  </div>
);
ModalName.js

ModalName is one of the modal components. It is built using React-Bootstrap's Modal and Button components. The step number and onClick handler are passed in as props from the parent component. The rest of the props (show and backdrop) are passed along to React-Bootstrap's Modal component. This uses ES'15 argument destructuring and ES'17 rest/spread. [ModalName.js on github]

const ModalName = ({ onClickNext, step, ...rest }) => (
  <Modal {...rest}>
    <Modal.Header closeButton>
      <Modal.Title>Step {step} - Name</Modal.Title>
    </Modal.Header>
    <Modal.Body>
      <p>Enter your name</p>
    </Modal.Body>
    <Modal.Footer>
      <Button bsStyle="primary" onClick={onClickNext}>Next</Button>
    </Modal.Footer>
  </Modal>
);
ChainedModals.js

ChainedModals keeps track of the current modal displayed in its component state. It defines _handleClickNext that is passed to the child modal components. When the child modal's "Next" button is clicked, this method is run. The method updates currIndex in the state, which causes a re-render. On re-render, ChainedModals finds a new modal component and renders it instead of the previous one. When the end of the modal list is reached, the modal is hidden, and the underlying page is shown. This uses ES'15 destructuring, ES'15 classes, and ES'17 class properties. [ChainedModals.js on github]

class ChainedModals extends Component {
  state = {
    currIndex: 0,
    showModal: true
  };

  render() {
    const { modalList } = this.props;
    const { currIndex, showModal } = this.state;
    const ModalComponent = modalList[currIndex];

    return (
      <div>
        {showModal && <ModalBackdrop />}
        <ModalComponent
          step={currIndex + 1}
          onClickNext={this._handleClickNext}
          backdrop={false}
          show={showModal}
        />
      </div>
    );
  }

  _handleClickNext = () => {
    const { modalList } = this.props;
    const { currIndex } = this.state;

    if (currIndex < modalList.length - 1) {
      this.setState({currIndex: currIndex + 1});
    } else {
      this.setState({showModal: false});
    }
  };
}
ModalBackdrop.js

A separate ModalBackdrop component is used so that the default modal's backdrop doesn't flash in and out when showing and hiding the modals. Since there is a separate modal backdrop, the backdrop prop is set to false for each modal. [ModalBackdrop.js on github]

const ModalBackdrop = () => <div className="modal-backdrop in"></div>;

Comments