<!-- What our app will be rendered into -->
<div id="app"></div>
.test-button{
  font-size: 20px;
  color: gray;
  padding: 5px;
  margin: 20px;
}
.modals {
  position: fixed;
  bottom:0;
  left:0;
  right:0;
  height: 300px;
}
.modal-wrapper{
  position: absolute;
  bottom:0;
  left:0;
  right:0;
  top: 0;
  background: rgba(0,0,0,0.3);
  display: flex;
  justify-content: center;
  align-items: center;
}
.modal {
  width: 200px;
  background: #fff;
  border-radius: 5px;
  min-width: 500px;
  position: relative;
}
.text {
  font-weight: bold;
  font-size: 20px;
  margin: 20px 0;
	text-align: center;
}
.buttons {
  margin: 10px 0;
  display: flex;
  justify-content: space-around;
}
.modal-button {
  font-size: 15px;
  color: gray;
  padding: 5px;
  border-radius: 5px;
}
.close {
  position: absolute;
  top: 10px;
  right: 0px;
  transform: translateY(-50%);
  background-color: transparent;
  border: none;
  color: #fff;
  font-size: 2em;
  cursor: pointer;
}
.modal-content {
  background:  rgba(193, 29, 29,0.5);
  color: #fff;
  height: 150px;
  display: flex;
  justify-content: center;
  align-items: center;
}
const { applyMiddleware, createStore, combineReducers, bindActionCreators } = Redux;
const { Provider, connect } = ReactRedux;
const { render } = ReactDOM;
const ReactDOM = ReactDOM;
const { Component } = React;

/* --- CONSTANTS --- */
const OPEN_MODAL = 'OPEN_MODAL';
const CLOSE_MODAL = 'CLOSE_MODAL';

/* --- REDUCERS --- */
const initialState = {
	modals: [],
}

function reducer(state = initialState, action) {
	switch (action.type) {
		case OPEN_MODAL:
			return {
				...state,
				modals: state.modals.concat(action.obj)
			};
		case CLOSE_MODAL:
			return {
				...state,
				modals: state.modals.filter(item => item.id !== action.obj.id),
			};
		default:
			return state;
	}
};

/* --- ACTIONS --- */
const openModal = (obj) => {
	return {
		type: OPEN_MODAL,
		obj,
	}
}
const closeModal = (obj) => {
	return {
		type: CLOSE_MODAL,
		obj,
	}
}

/* --- COMPONENTS --- */
class MyPortal extends React.PureComponent {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    document.body.appendChild(this.el);
  }

  componentWillUnmount() {
    document.body.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(this.props.children, this.el);
  }
}

class Modal extends Component {
  onClose(){
    if(this.props.item.onClose){
      this.props.item.onClose();
      this.props.onClose(this.props.item);
    } else {
      this.props.onClose(this.props.item);
    }
  }
  onConfirm(){
    if(this.props.item.onConfirm){
      this.props.item.onConfirm();
      this.props.onClose(this.props.item);
    }
  }
  render() {
    const { type } = this.props.item;
    if (type === 'confirmation') {
      const { text } = this.props.item;
      return (
        <div className="modal-wrapper">
          <div className="modal">
            <div className="text">{text}</div>
            <div className="buttons">
              <button className="modal-button" onClick={() => this.onConfirm()}>Confirm</button>
              <button className="modal-button" onClick={() => this.onClose()}>Close</button>
            </div>
          </div>
        </div>
      )
    } else if (type === 'custom') {
      const { content } = this.props.item;
      return (
        <div className="modal-wrapper">
          <div className="modal">
            <button className="close" onClick={() => this.onClose()}>&times;</button>
            {content}
          </div>
        </div>
      )
    }
    return (
        <div></div>
    );
  }
}
class Modals extends Component {
  render() {
    const modals = this.props.modals.map((item,i) => <MyPortal key={i} ><Modal item={item} onClose={(item) => this.props.dispatch(closeModal(item))}/></MyPortal>)
    return (
      <div className={modals.length>0 ? "modals" : ""}>
        {modals}
      </div>
    );
  }
}
const ModalContainer = connect(
	function mapStateToProps(state) {
		return {
			modals: state.modals
		};
	},
	function mapDispatchToProps(dispatch) {
		return {
			dispatch
		}
	}
)(Modals);


class CustomModalContent extends Component {
  render() {
    return (
      <div className="modal-content">Custom Modal Content</div>
    )
  }
}
class App extends Component {

  render() {
    return (
      <div className="App">
        <button className="test-button" onClick={() => this.props.dispatch(openModal({
          id: uuid.v4(),
          type: 'confirmation',
          text: 'Are you sure to do this?',
          onClose: () => console.log("fire at closing event"),
          onConfirm: () => console.log("fire at confirming event"),
        }))}>Open confirmation modal</button>

        <button className="test-button" onClick={() => this.props.dispatch(openModal({
          id: uuid.v4(),
          type: 'custom',
          content: <CustomModalContent />
        }))}>Open custom modal</button>

        <ModalContainer />
      </div>
    );
  }
}

const AppContainer = connect(
  null,
	function mapDispatchToProps(dispatch) {
    return {
      dispatch,
    }
	}
)(App);

/* --- OTHER --- */

/* --- STORE --- */
const store = createStore(reducer);

// Render the app
render(
	<Provider store={store}>
    <AppContainer />
  </Provider>,
	document.getElementById('app')
);
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/foundation/6.2.3/foundation.min.css

External JavaScript

  1. https://unpkg.com/react@16.0.0-rc.3/umd/react.development.js
  2. https://unpkg.com/react-dom@16.0.0-rc.3/umd/react-dom.development.js
  3. https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.7/uuid.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js
  5. https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.min.js
  6. https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.2.0/redux-thunk.min.js
  7. https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.min.js