Making Modals Stackable
Most of the time, you probably only need one modal open at once. But, what if you actually do need multiple modals open, stacked on top of each other? Fortunately, now that we’ve got the basic modal framework in place, this is pretty simple to add. All we really need to do have our reducer store an array of modal descriptions instead of just one, and have the ModalManager
component render multiple modals in response.
While we’re at it, we can update our TestModal
component to actually make use of the new functionality.
features/modals/modalReducer.js
-const initialState = null;
+const initialState = [];
export function openModal(state, payload) {
const {modalType, modalProps} = payload;
- return {modalType, modalProps};
+ // Always pushing a new modal onto the stack
+ return state.concat({modalType, modalProps});
}
export function closeModal(state, payload) {
- return null;
+ // Always popping the last modal off the stack
+ const newState = state.slice();
+ newState.pop();
+ return newState;
}
features/modals/ModalManager.jsx
const mapState = (state) => ({currentModals : state.modals});
export class ModalManager extends Component {
render() {
const {currentModals} = this.props;
const renderedModals = currentModals.map( (modalDescription, index) => {
const {modalType, modalProps = {}} = modalDescription;
const ModalComponent = modalComponentLookupTable[modalType];
return <ModalComponent {...modalProps} key={modalType + index}/>;
});
return <span>{renderedModals}</span>
}
}
Our modalReducer
cases switch to tracking a stack of modal descriptions in an array, and the ModalManager
component changes to loop over that array and render an array of modal components.
import {connect} from "react-redux";
import {
Modal,
+ Button,
} from "semantic-ui-react";
-import {closeModal} from "features/modals/modalActions";
+import {openModal, closeModal} from "features/modals/modalActions";
-const actions = {closeModal};
+const actions = {openModal, closeModal};
export class TestModal extends Component {
+ onNextModalClick = () => {
+ const {counter} = this.props;
+ this.props.openModal("TestModal", {counter : counter + 1});
+ }
render() {
+ const {counter, closeModal} = this.props;
return (
<Modal
closeIcon="close"
open={true}
- onClose={this.props.closeModal}
+ onClose={closeModal}
>
- <Modal.Header>Modal #1</Modal.Header>
+ <Modal.Header>Modal #{counter}</Modal.Header>
<Modal.Content image>
<Modal.Description>
- <p>This is a modal dialog. Pretty neat, huh?</p>
+ <h4>
+ Value from props:
+ </h4>
+ <div>
+ counter = {counter}
+ </div>
+ <div>
+ <Button onClick={this.onNextModalClick}>Add Another Modal</Button>
+ </div>
</Modal.Description>
</Modal.Content>
<Modal.Actions>
Our TestModal
gets changed in two ways. First, we’ll take the props.counter
value and display it as part of the header and the descriptive text. Then, we’ll add a button and handle clicks on it by dispatching an action to show another modal on top of the current one. To illustrate that we’re stacking them, we increase the counter value and pass it as a prop for the next modal in the stack.
Let’s try it out! If we click the “Show Test Modal” button in the Tools menu, and then click “Add Another Modal” a few times, here’s what we get (I temporarily added dimmer={false}
to TestModal
to keep the entire page from being blacked out):
Create a free account to access the full course.
By signing up, you agree to Educative's Terms of Service and Privacy Policy