How can I display a modal dialog in Redux that performs asynchronous actions?












211















I'm building an app that needs to show a confirm dialog in some situations.



Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id) so some reducer will catch that event and will fill the dialog reducer in order to show it.



My doubt comes when this dialog submits.




  • How can this component dispatch the proper action according to the first action dispatched?

  • Should the action creator handle this logic?

  • Can we add actions inside the reducer?


edit:



to make it clearer:



deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)


So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.










share|improve this question




















  • 1





    I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

    – Ming
    Sep 5 '16 at 5:51
















211















I'm building an app that needs to show a confirm dialog in some situations.



Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id) so some reducer will catch that event and will fill the dialog reducer in order to show it.



My doubt comes when this dialog submits.




  • How can this component dispatch the proper action according to the first action dispatched?

  • Should the action creator handle this logic?

  • Can we add actions inside the reducer?


edit:



to make it clearer:



deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)


So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.










share|improve this question




















  • 1





    I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

    – Ming
    Sep 5 '16 at 5:51














211












211








211


210






I'm building an app that needs to show a confirm dialog in some situations.



Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id) so some reducer will catch that event and will fill the dialog reducer in order to show it.



My doubt comes when this dialog submits.




  • How can this component dispatch the proper action according to the first action dispatched?

  • Should the action creator handle this logic?

  • Can we add actions inside the reducer?


edit:



to make it clearer:



deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)


So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.










share|improve this question
















I'm building an app that needs to show a confirm dialog in some situations.



Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id) so some reducer will catch that event and will fill the dialog reducer in order to show it.



My doubt comes when this dialog submits.




  • How can this component dispatch the proper action according to the first action dispatched?

  • Should the action creator handle this logic?

  • Can we add actions inside the reducer?


edit:



to make it clearer:



deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)


So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.







javascript modal-dialog redux react-redux






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 26 '16 at 1:24









Dan Abramov

179k48314448




179k48314448










asked Feb 25 '16 at 9:47









carlesbacarlesba

1,32931015




1,32931015








  • 1





    I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

    – Ming
    Sep 5 '16 at 5:51














  • 1





    I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

    – Ming
    Sep 5 '16 at 5:51








1




1





I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

– Ming
Sep 5 '16 at 5:51





I think in your case the state of dialog (hide/show) is local. I would choose to use the react state to manage dialog showing/hiding. In this way, the question of "proper action according to the first action" will be gone.

– Ming
Sep 5 '16 at 5:51












5 Answers
5






active

oldest

votes


















455














The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:



Dispatching an Action to Show the Modal



this.props.dispatch({
type: 'SHOW_MODAL',
modalType: 'DELETE_POST',
modalProps: {
postId: 42
}
})


(Strings can be constants of course; I’m using inline strings for simplicity.)



Writing a Reducer to Manage Modal State



Then make sure you have a reducer that just accepts these values:



const initialState = {
modalType: null,
modalProps: {}
}

function modal(state = initialState, action) {
switch (action.type) {
case 'SHOW_MODAL':
return {
modalType: action.modalType,
modalProps: action.modalProps
}
case 'HIDE_MODAL':
return initialState
default:
return state
}
}

/* .... */

const rootReducer = combineReducers({
modal,
/* other reducers */
})


Great! Now, when you dispatch an action, state.modal will update to include the information about the currently visible modal window.



Writing the Root Modal Component



At the root of your component hierarchy, add a <ModalRoot> component that is connected to the Redux store. It will listen to state.modal and display an appropriate modal component, forwarding the props from the state.modal.modalProps.



// These are regular React components we will write soon
import DeletePostModal from './DeletePostModal'
import ConfirmLogoutModal from './ConfirmLogoutModal'

const MODAL_COMPONENTS = {
'DELETE_POST': DeletePostModal,
'CONFIRM_LOGOUT': ConfirmLogoutModal,
/* other modals */
}

const ModalRoot = ({ modalType, modalProps }) => {
if (!modalType) {
return <span /> // after React v15 you can return null here
}

const SpecificModal = MODAL_COMPONENTS[modalType]
return <SpecificModal {...modalProps} />
}

export default connect(
state => state.modal
)(ModalRoot)


What have we done here? ModalRoot reads the current modalType and modalProps from state.modal to which it is connected, and renders a corresponding component such as DeletePostModal or ConfirmLogoutModal. Every modal is a component!



Writing Specific Modal Components



There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be modals.



For example, DeletePostModal might look like:



import { deletePost, hideModal } from '../actions'

const DeletePostModal = ({ post, dispatch }) => (
<div>
<p>Delete post {post.name}?</p>
<button onClick={() => {
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
}}>
Yes
</button>
<button onClick={() => dispatch(hideModal())}>
Nope
</button>
</div>
)

export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)


The DeletePostModal is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal when it is necessary to hide itself.



Extracting a Presentational Component



It would be awkward to copy-paste the same layout logic for every “specific” modal. But you have components, right? So you can extract a presentational <Modal> component that doesn’t know what particular modals do, but handles how they look.



Then, specific modals such as DeletePostModal can use it for rendering:



import { deletePost, hideModal } from '../actions'
import Modal from './Modal'

const DeletePostModal = ({ post, dispatch }) => (
<Modal
dangerText={`Delete post ${post.name}?`}
onDangerClick={() =>
dispatch(deletePost(post.id)).then(() => {
dispatch(hideModal())
})
})
/>
)

export default connect(
(state, ownProps) => ({
post: state.postsById[ownProps.postId]
})
)(DeletePostModal)


It is up to you to come up with a set of props that <Modal> can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.



Accessibility and Hiding on Click Outside or Escape Key



The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.



Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.



Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.



You can even wrap react-modal in your own <Modal> that accepts props specific to your applications and generates child buttons or other content. It’s all just components!



Other Approaches



There is more than one way to do it.



Some people don’t like the verbosity of this approach and prefer to have a <Modal> component that they can render right inside their components with a technique called “portals”. Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.



In fact react-modal I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal directly from your components, and skip most of what I wrote above.



I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.






share|improve this answer





















  • 31





    One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

    – Kyle
    Feb 26 '16 at 1:46






  • 8





    Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

    – Dan Abramov
    Feb 26 '16 at 1:56






  • 3





    In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

    – Sebastien Lorber
    Feb 26 '16 at 10:45






  • 4





    @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

    – gcerar
    Mar 6 '16 at 21:21






  • 3





    Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

    – Brett Mathe
    Oct 16 '16 at 21:56





















85














Update: React 16.0 introduced portals through ReactDOM.createPortal link



Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link





Use portals



Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.



The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.



What is a portal?



A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.



The idea is that for example you render into body the following React tree:



<div className="layout">
<div className="outside-portal">
<Portal>
<div className="inside-portal">
PortalContent
</div>
</Portal>
</div>
</div>


And you get as output:



<body>
<div class="layout">
<div class="outside-portal">
</div>
</div>
<div class="inside-portal">
PortalContent
</div>
</body>


The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.



When to use a portal



A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots



Why use a portal



No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.



In practice



Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:



class DeleteButton extends React.Component {
static propTypes = {
onDelete: PropTypes.func.isRequired,
};

state = { confirmationPopup: false };

open = () => {
this.setState({ confirmationPopup: true });
};

close = () => {
this.setState({ confirmationPopup: false });
};

render() {
return (
<div className="delete-button">
<div onClick={() => this.open()}>Delete</div>
{this.state.confirmationPopup && (
<Portal>
<DeleteConfirmationPopup
onCancel={() => this.close()}
onConfirm={() => {
this.close();
this.props.onDelete();
}}
/>
</Portal>
)}
</div>
);
}
}


Simple: you can still use Redux state: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton



Conclusion



Portals are very useful to simplify your code. I couldn't do without them anymore.



Note that portal implementations can also help you with other useful features like:




  • Accessibility

  • Espace shortcuts to close the portal

  • Handle outside click (close portal or not)

  • Handle link click (close portal or not)

  • React Context made available in portal tree


react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.



react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.



You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:



Onboarding hotspot



Real production code here. Can't be any simpler :)



<MenuHotspots.contacts>
<ContactButton/>
</MenuHotspots.contacts>




Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)



Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.



Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree






share|improve this answer


























  • In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

    – dKab
    Dec 29 '16 at 9:13











  • It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

    – stone
    Mar 15 '17 at 19:54






  • 1





    @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

    – Sebastien Lorber
    Mar 15 '17 at 20:33











  • react-gateway is awesome! It support server side rendering :)

    – cyrilluce
    Jun 17 '17 at 10:21



















8














A lot of good solutions and valuable commentaries by known experts from JS community on the topic could be found here. It could be an indicator that it's not that trivial problem as it may seem. I think this is why it could be the source of doubts and uncertainty on the issue.



Fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?



I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale




Rationale



react/react-dom comes comes with 2 basic assumptions/ideas:




  • every UI is hierarchical naturally. This why we have the idea of components which wrap each other


  • react-dom mounts (physically) child component to its parent DOM node by default


The problem is that sometimes the second property isn't what you want
in your case. Sometimes you want to mount your component into
different physical DOM node and hold logical connection between
parent and child at the same time.



Canonical example is Tooltip-like component: at some point of
development process you could find that you need to add some
description for your UI element: it'll render in fixed layer and
should know its coordinates (which are that UI element coord or
mouse coords) and at the same time it needs information whether it
needs to be shown right now or not, its content and some context from
parent components. This example shows that sometimes logical hierarchy
isn't match with the physical DOM hierarchy.




Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question:



import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
return (
<Cell {...props}>
// the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
<Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
hideMe, // alias for `hide(modalId)`
index } // useful to know to set zIndex, for example
, e) => // access to the arguments (click event data in this example)
<Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
<ConfirmationDialog
title={ 'Delete' }
message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
confirmButton={ <Button type="primary">DELETE</Button> }
onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
close={ hideMe } />
</Modal> }
</Layer>

// this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
<LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
<div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
<Icon type="trash" />
</div> }
</LayerContext>
</Cell>)
// ...





share|improve this answer

































    1














    In my opinion the bare minimum implementation has two requirements. A state that keeps track of whether the modal is open or not, and a portal to render the modal outside of the standard react tree.



    The ModalContainer component below implements those requirements along with corresponding render functions for the modal and the trigger, which is responsible for executing the callback to open the modal.



    import React from 'react';
    import PropTypes from 'prop-types';
    import Portal from 'react-portal';

    class ModalContainer extends React.Component {
    state = {
    isOpen: false,
    };

    openModal = () => {
    this.setState(() => ({ isOpen: true }));
    }

    closeModal = () => {
    this.setState(() => ({ isOpen: false }));
    }

    renderModal() {
    return (
    this.props.renderModal({
    isOpen: this.state.isOpen,
    closeModal: this.closeModal,
    })
    );
    }

    renderTrigger() {
    return (
    this.props.renderTrigger({
    openModal: this.openModal
    })
    )
    }

    render() {
    return (
    <React.Fragment>
    <Portal>
    {this.renderModal()}
    </Portal>
    {this.renderTrigger()}
    </React.Fragment>
    );
    }
    }

    ModalContainer.propTypes = {
    renderModal: PropTypes.func.isRequired,
    renderTrigger: PropTypes.func.isRequired,
    };

    export default ModalContainer;


    And here's a simple use case...



    import React from 'react';
    import Modal from 'react-modal';
    import Fade from 'components/Animations/Fade';
    import ModalContainer from 'components/ModalContainer';

    const SimpleModal = ({ isOpen, closeModal }) => (
    <Fade visible={isOpen}> // example use case with animation components
    <Modal>
    <Button onClick={closeModal}>
    close modal
    </Button>
    </Modal>
    </Fade>
    );

    const SimpleModalButton = ({ openModal }) => (
    <button onClick={openModal}>
    open modal
    </button>
    );

    const SimpleButtonWithModal = () => (
    <ModalContainer
    renderModal={props => <SimpleModal {...props} />}
    renderTrigger={props => <SimpleModalButton {...props} />}
    />
    );

    export default SimpleButtonWithModal;


    I use render functions, because I want to isolate state management and boilerplate logic from the implementation of the rendered modal and trigger component. This allows the rendered components to be whatever you want them to be. In your case, I assume the modal component would be a connected component that receives a callback function that dispatches an asynchronous action.



    If you need to send dynamic props to the modal component from the trigger component, which hopefully doesn't happen too often, I recommend wrapping the ModalContainer with a container component that manages the dynamic props in its own state and enhance the original render methods like so.



    import React from 'react'
    import partialRight from 'lodash/partialRight';
    import ModalContainer from 'components/ModalContainer';

    class ErrorModalContainer extends React.Component {
    state = { message: '' }

    onError = (message, callback) => {
    this.setState(
    () => ({ message }),
    () => callback && callback()
    );
    }

    renderModal = (props) => (
    this.props.renderModal({
    ...props,
    message: this.state.message,
    })
    )

    renderTrigger = (props) => (
    this.props.renderTrigger({
    openModal: partialRight(this.onError, props.openModal)
    })
    )

    render() {
    return (
    <ModalContainer
    renderModal={this.renderModal}
    renderTrigger={this.renderTrigger}
    />
    )
    }
    }

    ErrorModalContainer.propTypes = (
    ModalContainer.propTypes
    );

    export default ErrorModalContainer;





    share|improve this answer

































      0














      Wrap the modal into a connected container and perform the async operation in here. This way you can reach both the dispatch to trigger actions, and the onClose prop too. (To reach dispatch from props, do not passs mapDispatchToProps function to connect.



      class ModalConteiner extends React.Component {
      handleDelete = () => {
      const { dispatch, onClose } = this.props;
      dispatch({type: 'DELETE_POST'});

      someAsyncOperation().then(() => {
      dispatch({type: 'DELETE_POST_SUCCESS'});
      onClose();
      })
      }

      render() {
      const { onClose } = this.props;
      return <Modal onClose={onClose} onSubmit={this.handleDelete} />
      }
      }

      export default connect(/* no map dispatch to props here! */)(ModalContainer);




      The app where the modal is rendered and its visibility state is set:



      class App extends React.Component {
      state = {
      isModalOpen: false
      }

      handleModalClose = () => this.setState({ isModalOpen: false });

      ...

      render(){
      return (
      ...
      <ModalContainer onClose={this.handleModalClose} />
      ...
      )
      }

      }







      share|improve this answer























        Your Answer






        StackExchange.ifUsing("editor", function () {
        StackExchange.using("externalEditor", function () {
        StackExchange.using("snippets", function () {
        StackExchange.snippets.init();
        });
        });
        }, "code-snippets");

        StackExchange.ready(function() {
        var channelOptions = {
        tags: "".split(" "),
        id: "1"
        };
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function() {
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled) {
        StackExchange.using("snippets", function() {
        createEditor();
        });
        }
        else {
        createEditor();
        }
        });

        function createEditor() {
        StackExchange.prepareEditor({
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader: {
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        },
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        });


        }
        });














        draft saved

        draft discarded


















        StackExchange.ready(
        function () {
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f35623656%2fhow-can-i-display-a-modal-dialog-in-redux-that-performs-asynchronous-actions%23new-answer', 'question_page');
        }
        );

        Post as a guest















        Required, but never shown

























        5 Answers
        5






        active

        oldest

        votes








        5 Answers
        5






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        455














        The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:



        Dispatching an Action to Show the Modal



        this.props.dispatch({
        type: 'SHOW_MODAL',
        modalType: 'DELETE_POST',
        modalProps: {
        postId: 42
        }
        })


        (Strings can be constants of course; I’m using inline strings for simplicity.)



        Writing a Reducer to Manage Modal State



        Then make sure you have a reducer that just accepts these values:



        const initialState = {
        modalType: null,
        modalProps: {}
        }

        function modal(state = initialState, action) {
        switch (action.type) {
        case 'SHOW_MODAL':
        return {
        modalType: action.modalType,
        modalProps: action.modalProps
        }
        case 'HIDE_MODAL':
        return initialState
        default:
        return state
        }
        }

        /* .... */

        const rootReducer = combineReducers({
        modal,
        /* other reducers */
        })


        Great! Now, when you dispatch an action, state.modal will update to include the information about the currently visible modal window.



        Writing the Root Modal Component



        At the root of your component hierarchy, add a <ModalRoot> component that is connected to the Redux store. It will listen to state.modal and display an appropriate modal component, forwarding the props from the state.modal.modalProps.



        // These are regular React components we will write soon
        import DeletePostModal from './DeletePostModal'
        import ConfirmLogoutModal from './ConfirmLogoutModal'

        const MODAL_COMPONENTS = {
        'DELETE_POST': DeletePostModal,
        'CONFIRM_LOGOUT': ConfirmLogoutModal,
        /* other modals */
        }

        const ModalRoot = ({ modalType, modalProps }) => {
        if (!modalType) {
        return <span /> // after React v15 you can return null here
        }

        const SpecificModal = MODAL_COMPONENTS[modalType]
        return <SpecificModal {...modalProps} />
        }

        export default connect(
        state => state.modal
        )(ModalRoot)


        What have we done here? ModalRoot reads the current modalType and modalProps from state.modal to which it is connected, and renders a corresponding component such as DeletePostModal or ConfirmLogoutModal. Every modal is a component!



        Writing Specific Modal Components



        There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be modals.



        For example, DeletePostModal might look like:



        import { deletePost, hideModal } from '../actions'

        const DeletePostModal = ({ post, dispatch }) => (
        <div>
        <p>Delete post {post.name}?</p>
        <button onClick={() => {
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        }}>
        Yes
        </button>
        <button onClick={() => dispatch(hideModal())}>
        Nope
        </button>
        </div>
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        The DeletePostModal is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal when it is necessary to hide itself.



        Extracting a Presentational Component



        It would be awkward to copy-paste the same layout logic for every “specific” modal. But you have components, right? So you can extract a presentational <Modal> component that doesn’t know what particular modals do, but handles how they look.



        Then, specific modals such as DeletePostModal can use it for rendering:



        import { deletePost, hideModal } from '../actions'
        import Modal from './Modal'

        const DeletePostModal = ({ post, dispatch }) => (
        <Modal
        dangerText={`Delete post ${post.name}?`}
        onDangerClick={() =>
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        })
        />
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        It is up to you to come up with a set of props that <Modal> can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.



        Accessibility and Hiding on Click Outside or Escape Key



        The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.



        Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.



        Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.



        You can even wrap react-modal in your own <Modal> that accepts props specific to your applications and generates child buttons or other content. It’s all just components!



        Other Approaches



        There is more than one way to do it.



        Some people don’t like the verbosity of this approach and prefer to have a <Modal> component that they can render right inside their components with a technique called “portals”. Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.



        In fact react-modal I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal directly from your components, and skip most of what I wrote above.



        I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.






        share|improve this answer





















        • 31





          One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

          – Kyle
          Feb 26 '16 at 1:46






        • 8





          Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

          – Dan Abramov
          Feb 26 '16 at 1:56






        • 3





          In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

          – Sebastien Lorber
          Feb 26 '16 at 10:45






        • 4





          @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

          – gcerar
          Mar 6 '16 at 21:21






        • 3





          Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

          – Brett Mathe
          Oct 16 '16 at 21:56


















        455














        The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:



        Dispatching an Action to Show the Modal



        this.props.dispatch({
        type: 'SHOW_MODAL',
        modalType: 'DELETE_POST',
        modalProps: {
        postId: 42
        }
        })


        (Strings can be constants of course; I’m using inline strings for simplicity.)



        Writing a Reducer to Manage Modal State



        Then make sure you have a reducer that just accepts these values:



        const initialState = {
        modalType: null,
        modalProps: {}
        }

        function modal(state = initialState, action) {
        switch (action.type) {
        case 'SHOW_MODAL':
        return {
        modalType: action.modalType,
        modalProps: action.modalProps
        }
        case 'HIDE_MODAL':
        return initialState
        default:
        return state
        }
        }

        /* .... */

        const rootReducer = combineReducers({
        modal,
        /* other reducers */
        })


        Great! Now, when you dispatch an action, state.modal will update to include the information about the currently visible modal window.



        Writing the Root Modal Component



        At the root of your component hierarchy, add a <ModalRoot> component that is connected to the Redux store. It will listen to state.modal and display an appropriate modal component, forwarding the props from the state.modal.modalProps.



        // These are regular React components we will write soon
        import DeletePostModal from './DeletePostModal'
        import ConfirmLogoutModal from './ConfirmLogoutModal'

        const MODAL_COMPONENTS = {
        'DELETE_POST': DeletePostModal,
        'CONFIRM_LOGOUT': ConfirmLogoutModal,
        /* other modals */
        }

        const ModalRoot = ({ modalType, modalProps }) => {
        if (!modalType) {
        return <span /> // after React v15 you can return null here
        }

        const SpecificModal = MODAL_COMPONENTS[modalType]
        return <SpecificModal {...modalProps} />
        }

        export default connect(
        state => state.modal
        )(ModalRoot)


        What have we done here? ModalRoot reads the current modalType and modalProps from state.modal to which it is connected, and renders a corresponding component such as DeletePostModal or ConfirmLogoutModal. Every modal is a component!



        Writing Specific Modal Components



        There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be modals.



        For example, DeletePostModal might look like:



        import { deletePost, hideModal } from '../actions'

        const DeletePostModal = ({ post, dispatch }) => (
        <div>
        <p>Delete post {post.name}?</p>
        <button onClick={() => {
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        }}>
        Yes
        </button>
        <button onClick={() => dispatch(hideModal())}>
        Nope
        </button>
        </div>
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        The DeletePostModal is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal when it is necessary to hide itself.



        Extracting a Presentational Component



        It would be awkward to copy-paste the same layout logic for every “specific” modal. But you have components, right? So you can extract a presentational <Modal> component that doesn’t know what particular modals do, but handles how they look.



        Then, specific modals such as DeletePostModal can use it for rendering:



        import { deletePost, hideModal } from '../actions'
        import Modal from './Modal'

        const DeletePostModal = ({ post, dispatch }) => (
        <Modal
        dangerText={`Delete post ${post.name}?`}
        onDangerClick={() =>
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        })
        />
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        It is up to you to come up with a set of props that <Modal> can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.



        Accessibility and Hiding on Click Outside or Escape Key



        The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.



        Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.



        Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.



        You can even wrap react-modal in your own <Modal> that accepts props specific to your applications and generates child buttons or other content. It’s all just components!



        Other Approaches



        There is more than one way to do it.



        Some people don’t like the verbosity of this approach and prefer to have a <Modal> component that they can render right inside their components with a technique called “portals”. Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.



        In fact react-modal I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal directly from your components, and skip most of what I wrote above.



        I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.






        share|improve this answer





















        • 31





          One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

          – Kyle
          Feb 26 '16 at 1:46






        • 8





          Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

          – Dan Abramov
          Feb 26 '16 at 1:56






        • 3





          In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

          – Sebastien Lorber
          Feb 26 '16 at 10:45






        • 4





          @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

          – gcerar
          Mar 6 '16 at 21:21






        • 3





          Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

          – Brett Mathe
          Oct 16 '16 at 21:56
















        455












        455








        455







        The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:



        Dispatching an Action to Show the Modal



        this.props.dispatch({
        type: 'SHOW_MODAL',
        modalType: 'DELETE_POST',
        modalProps: {
        postId: 42
        }
        })


        (Strings can be constants of course; I’m using inline strings for simplicity.)



        Writing a Reducer to Manage Modal State



        Then make sure you have a reducer that just accepts these values:



        const initialState = {
        modalType: null,
        modalProps: {}
        }

        function modal(state = initialState, action) {
        switch (action.type) {
        case 'SHOW_MODAL':
        return {
        modalType: action.modalType,
        modalProps: action.modalProps
        }
        case 'HIDE_MODAL':
        return initialState
        default:
        return state
        }
        }

        /* .... */

        const rootReducer = combineReducers({
        modal,
        /* other reducers */
        })


        Great! Now, when you dispatch an action, state.modal will update to include the information about the currently visible modal window.



        Writing the Root Modal Component



        At the root of your component hierarchy, add a <ModalRoot> component that is connected to the Redux store. It will listen to state.modal and display an appropriate modal component, forwarding the props from the state.modal.modalProps.



        // These are regular React components we will write soon
        import DeletePostModal from './DeletePostModal'
        import ConfirmLogoutModal from './ConfirmLogoutModal'

        const MODAL_COMPONENTS = {
        'DELETE_POST': DeletePostModal,
        'CONFIRM_LOGOUT': ConfirmLogoutModal,
        /* other modals */
        }

        const ModalRoot = ({ modalType, modalProps }) => {
        if (!modalType) {
        return <span /> // after React v15 you can return null here
        }

        const SpecificModal = MODAL_COMPONENTS[modalType]
        return <SpecificModal {...modalProps} />
        }

        export default connect(
        state => state.modal
        )(ModalRoot)


        What have we done here? ModalRoot reads the current modalType and modalProps from state.modal to which it is connected, and renders a corresponding component such as DeletePostModal or ConfirmLogoutModal. Every modal is a component!



        Writing Specific Modal Components



        There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be modals.



        For example, DeletePostModal might look like:



        import { deletePost, hideModal } from '../actions'

        const DeletePostModal = ({ post, dispatch }) => (
        <div>
        <p>Delete post {post.name}?</p>
        <button onClick={() => {
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        }}>
        Yes
        </button>
        <button onClick={() => dispatch(hideModal())}>
        Nope
        </button>
        </div>
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        The DeletePostModal is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal when it is necessary to hide itself.



        Extracting a Presentational Component



        It would be awkward to copy-paste the same layout logic for every “specific” modal. But you have components, right? So you can extract a presentational <Modal> component that doesn’t know what particular modals do, but handles how they look.



        Then, specific modals such as DeletePostModal can use it for rendering:



        import { deletePost, hideModal } from '../actions'
        import Modal from './Modal'

        const DeletePostModal = ({ post, dispatch }) => (
        <Modal
        dangerText={`Delete post ${post.name}?`}
        onDangerClick={() =>
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        })
        />
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        It is up to you to come up with a set of props that <Modal> can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.



        Accessibility and Hiding on Click Outside or Escape Key



        The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.



        Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.



        Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.



        You can even wrap react-modal in your own <Modal> that accepts props specific to your applications and generates child buttons or other content. It’s all just components!



        Other Approaches



        There is more than one way to do it.



        Some people don’t like the verbosity of this approach and prefer to have a <Modal> component that they can render right inside their components with a technique called “portals”. Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.



        In fact react-modal I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal directly from your components, and skip most of what I wrote above.



        I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.






        share|improve this answer















        The approach I suggest is a bit verbose but I found it to scale pretty well into complex apps. When you want to show a modal, fire an action describing which modal you'd like to see:



        Dispatching an Action to Show the Modal



        this.props.dispatch({
        type: 'SHOW_MODAL',
        modalType: 'DELETE_POST',
        modalProps: {
        postId: 42
        }
        })


        (Strings can be constants of course; I’m using inline strings for simplicity.)



        Writing a Reducer to Manage Modal State



        Then make sure you have a reducer that just accepts these values:



        const initialState = {
        modalType: null,
        modalProps: {}
        }

        function modal(state = initialState, action) {
        switch (action.type) {
        case 'SHOW_MODAL':
        return {
        modalType: action.modalType,
        modalProps: action.modalProps
        }
        case 'HIDE_MODAL':
        return initialState
        default:
        return state
        }
        }

        /* .... */

        const rootReducer = combineReducers({
        modal,
        /* other reducers */
        })


        Great! Now, when you dispatch an action, state.modal will update to include the information about the currently visible modal window.



        Writing the Root Modal Component



        At the root of your component hierarchy, add a <ModalRoot> component that is connected to the Redux store. It will listen to state.modal and display an appropriate modal component, forwarding the props from the state.modal.modalProps.



        // These are regular React components we will write soon
        import DeletePostModal from './DeletePostModal'
        import ConfirmLogoutModal from './ConfirmLogoutModal'

        const MODAL_COMPONENTS = {
        'DELETE_POST': DeletePostModal,
        'CONFIRM_LOGOUT': ConfirmLogoutModal,
        /* other modals */
        }

        const ModalRoot = ({ modalType, modalProps }) => {
        if (!modalType) {
        return <span /> // after React v15 you can return null here
        }

        const SpecificModal = MODAL_COMPONENTS[modalType]
        return <SpecificModal {...modalProps} />
        }

        export default connect(
        state => state.modal
        )(ModalRoot)


        What have we done here? ModalRoot reads the current modalType and modalProps from state.modal to which it is connected, and renders a corresponding component such as DeletePostModal or ConfirmLogoutModal. Every modal is a component!



        Writing Specific Modal Components



        There are no general rules here. They are just React components that can dispatch actions, read something from the store state, and just happen to be modals.



        For example, DeletePostModal might look like:



        import { deletePost, hideModal } from '../actions'

        const DeletePostModal = ({ post, dispatch }) => (
        <div>
        <p>Delete post {post.name}?</p>
        <button onClick={() => {
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        }}>
        Yes
        </button>
        <button onClick={() => dispatch(hideModal())}>
        Nope
        </button>
        </div>
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        The DeletePostModal is connected to the store so it can display the post title and works like any connected component: it can dispatch actions, including hideModal when it is necessary to hide itself.



        Extracting a Presentational Component



        It would be awkward to copy-paste the same layout logic for every “specific” modal. But you have components, right? So you can extract a presentational <Modal> component that doesn’t know what particular modals do, but handles how they look.



        Then, specific modals such as DeletePostModal can use it for rendering:



        import { deletePost, hideModal } from '../actions'
        import Modal from './Modal'

        const DeletePostModal = ({ post, dispatch }) => (
        <Modal
        dangerText={`Delete post ${post.name}?`}
        onDangerClick={() =>
        dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
        })
        })
        />
        )

        export default connect(
        (state, ownProps) => ({
        post: state.postsById[ownProps.postId]
        })
        )(DeletePostModal)


        It is up to you to come up with a set of props that <Modal> can accept in your application but I would imagine that you might have several kinds of modals (e.g. info modal, confirmation modal, etc), and several styles for them.



        Accessibility and Hiding on Click Outside or Escape Key



        The last important part about modals is that generally we want to hide them when the user clicks outside or presses Escape.



        Instead of giving you advice on implementing this, I suggest that you just don’t implement it yourself. It is hard to get right considering accessibility.



        Instead, I would suggest you to use an accessible off-the-shelf modal component such as react-modal. It is completely customizable, you can put anything you want inside of it, but it handles accessibility correctly so that blind people can still use your modal.



        You can even wrap react-modal in your own <Modal> that accepts props specific to your applications and generates child buttons or other content. It’s all just components!



        Other Approaches



        There is more than one way to do it.



        Some people don’t like the verbosity of this approach and prefer to have a <Modal> component that they can render right inside their components with a technique called “portals”. Portals let you render a component inside yours while actually it will render at a predetermined place in the DOM, which is very convenient for modals.



        In fact react-modal I linked to earlier already does that internally so technically you don’t even need to render it from the top. I still find it nice to decouple the modal I want to show from the component showing it, but you can also use react-modal directly from your components, and skip most of what I wrote above.



        I encourage you to consider both approaches, experiment with them, and pick what you find works best for your app and for your team.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jun 30 '17 at 15:15









        Alireza

        51k13174123




        51k13174123










        answered Feb 26 '16 at 1:22









        Dan AbramovDan Abramov

        179k48314448




        179k48314448








        • 31





          One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

          – Kyle
          Feb 26 '16 at 1:46






        • 8





          Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

          – Dan Abramov
          Feb 26 '16 at 1:56






        • 3





          In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

          – Sebastien Lorber
          Feb 26 '16 at 10:45






        • 4





          @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

          – gcerar
          Mar 6 '16 at 21:21






        • 3





          Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

          – Brett Mathe
          Oct 16 '16 at 21:56
















        • 31





          One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

          – Kyle
          Feb 26 '16 at 1:46






        • 8





          Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

          – Dan Abramov
          Feb 26 '16 at 1:56






        • 3





          In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

          – Sebastien Lorber
          Feb 26 '16 at 10:45






        • 4





          @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

          – gcerar
          Mar 6 '16 at 21:21






        • 3





          Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

          – Brett Mathe
          Oct 16 '16 at 21:56










        31




        31





        One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

        – Kyle
        Feb 26 '16 at 1:46





        One thing I'd suggest is having the reducer maintain a list of modals that can be pushed and popped. As silly as it sounds, I've consistently run into situations where designers/product types want me to open a modal from a modal, and it nice to allow users to "go back".

        – Kyle
        Feb 26 '16 at 1:46




        8




        8





        Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

        – Dan Abramov
        Feb 26 '16 at 1:56





        Yeah, definitely, this is the kind of thing Redux makes easy to build because you can just change your state to be an array. Personally I’ve worked with designers who, on the opposite, wanted modals to be exclusive, so the approach I wrote up solves accidental nesting. But yeah, you can have it both ways.

        – Dan Abramov
        Feb 26 '16 at 1:56




        3




        3





        In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

        – Sebastien Lorber
        Feb 26 '16 at 10:45





        In my experience I'd say: if modal is related to a local component (like a delete confirmation modal is related to the delete button), it's simpler to use a portal, else use redux actions. Agree with @Kyle one should be able to open a modal from a modal. It also works by default with portals because they are added in order to document body so portals stack on each others nicely (until you mess everything up with z-index :p)

        – Sebastien Lorber
        Feb 26 '16 at 10:45




        4




        4





        @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

        – gcerar
        Mar 6 '16 at 21:21





        @DanAbramov, your solution is great, but I have minor problem. Nothing serious. I use Material-ui in project, when closing modal it just shut it off, instead of "playing" fade-away animation. Probably need to do some sort of delay? Or keep every modal there as a list inside of ModalRoot? Suggestions?

        – gcerar
        Mar 6 '16 at 21:21




        3




        3





        Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

        – Brett Mathe
        Oct 16 '16 at 21:56







        Is there a way to pass in a function from the parent container/compoonent that can be called once the modal is closed? I can call actions from the modal on button presses but I would like a callback when the modal closes that will allow me to execute a function on the parent. Parent loads -> button click dispatches modal -> on confirm on modal dispatches an action and then dispatches hideModal -> parent function in container is called and if confirm was success then the page is redirected..

        – Brett Mathe
        Oct 16 '16 at 21:56















        85














        Update: React 16.0 introduced portals through ReactDOM.createPortal link



        Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link





        Use portals



        Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.



        The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.



        What is a portal?



        A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.



        The idea is that for example you render into body the following React tree:



        <div className="layout">
        <div className="outside-portal">
        <Portal>
        <div className="inside-portal">
        PortalContent
        </div>
        </Portal>
        </div>
        </div>


        And you get as output:



        <body>
        <div class="layout">
        <div class="outside-portal">
        </div>
        </div>
        <div class="inside-portal">
        PortalContent
        </div>
        </body>


        The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.



        When to use a portal



        A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots



        Why use a portal



        No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.



        In practice



        Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:



        class DeleteButton extends React.Component {
        static propTypes = {
        onDelete: PropTypes.func.isRequired,
        };

        state = { confirmationPopup: false };

        open = () => {
        this.setState({ confirmationPopup: true });
        };

        close = () => {
        this.setState({ confirmationPopup: false });
        };

        render() {
        return (
        <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
        <Portal>
        <DeleteConfirmationPopup
        onCancel={() => this.close()}
        onConfirm={() => {
        this.close();
        this.props.onDelete();
        }}
        />
        </Portal>
        )}
        </div>
        );
        }
        }


        Simple: you can still use Redux state: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton



        Conclusion



        Portals are very useful to simplify your code. I couldn't do without them anymore.



        Note that portal implementations can also help you with other useful features like:




        • Accessibility

        • Espace shortcuts to close the portal

        • Handle outside click (close portal or not)

        • Handle link click (close portal or not)

        • React Context made available in portal tree


        react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.



        react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.



        You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:



        Onboarding hotspot



        Real production code here. Can't be any simpler :)



        <MenuHotspots.contacts>
        <ContactButton/>
        </MenuHotspots.contacts>




        Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)



        Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.



        Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree






        share|improve this answer


























        • In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

          – dKab
          Dec 29 '16 at 9:13











        • It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

          – stone
          Mar 15 '17 at 19:54






        • 1





          @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

          – Sebastien Lorber
          Mar 15 '17 at 20:33











        • react-gateway is awesome! It support server side rendering :)

          – cyrilluce
          Jun 17 '17 at 10:21
















        85














        Update: React 16.0 introduced portals through ReactDOM.createPortal link



        Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link





        Use portals



        Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.



        The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.



        What is a portal?



        A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.



        The idea is that for example you render into body the following React tree:



        <div className="layout">
        <div className="outside-portal">
        <Portal>
        <div className="inside-portal">
        PortalContent
        </div>
        </Portal>
        </div>
        </div>


        And you get as output:



        <body>
        <div class="layout">
        <div class="outside-portal">
        </div>
        </div>
        <div class="inside-portal">
        PortalContent
        </div>
        </body>


        The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.



        When to use a portal



        A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots



        Why use a portal



        No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.



        In practice



        Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:



        class DeleteButton extends React.Component {
        static propTypes = {
        onDelete: PropTypes.func.isRequired,
        };

        state = { confirmationPopup: false };

        open = () => {
        this.setState({ confirmationPopup: true });
        };

        close = () => {
        this.setState({ confirmationPopup: false });
        };

        render() {
        return (
        <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
        <Portal>
        <DeleteConfirmationPopup
        onCancel={() => this.close()}
        onConfirm={() => {
        this.close();
        this.props.onDelete();
        }}
        />
        </Portal>
        )}
        </div>
        );
        }
        }


        Simple: you can still use Redux state: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton



        Conclusion



        Portals are very useful to simplify your code. I couldn't do without them anymore.



        Note that portal implementations can also help you with other useful features like:




        • Accessibility

        • Espace shortcuts to close the portal

        • Handle outside click (close portal or not)

        • Handle link click (close portal or not)

        • React Context made available in portal tree


        react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.



        react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.



        You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:



        Onboarding hotspot



        Real production code here. Can't be any simpler :)



        <MenuHotspots.contacts>
        <ContactButton/>
        </MenuHotspots.contacts>




        Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)



        Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.



        Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree






        share|improve this answer


























        • In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

          – dKab
          Dec 29 '16 at 9:13











        • It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

          – stone
          Mar 15 '17 at 19:54






        • 1





          @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

          – Sebastien Lorber
          Mar 15 '17 at 20:33











        • react-gateway is awesome! It support server side rendering :)

          – cyrilluce
          Jun 17 '17 at 10:21














        85












        85








        85







        Update: React 16.0 introduced portals through ReactDOM.createPortal link



        Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link





        Use portals



        Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.



        The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.



        What is a portal?



        A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.



        The idea is that for example you render into body the following React tree:



        <div className="layout">
        <div className="outside-portal">
        <Portal>
        <div className="inside-portal">
        PortalContent
        </div>
        </Portal>
        </div>
        </div>


        And you get as output:



        <body>
        <div class="layout">
        <div class="outside-portal">
        </div>
        </div>
        <div class="inside-portal">
        PortalContent
        </div>
        </body>


        The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.



        When to use a portal



        A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots



        Why use a portal



        No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.



        In practice



        Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:



        class DeleteButton extends React.Component {
        static propTypes = {
        onDelete: PropTypes.func.isRequired,
        };

        state = { confirmationPopup: false };

        open = () => {
        this.setState({ confirmationPopup: true });
        };

        close = () => {
        this.setState({ confirmationPopup: false });
        };

        render() {
        return (
        <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
        <Portal>
        <DeleteConfirmationPopup
        onCancel={() => this.close()}
        onConfirm={() => {
        this.close();
        this.props.onDelete();
        }}
        />
        </Portal>
        )}
        </div>
        );
        }
        }


        Simple: you can still use Redux state: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton



        Conclusion



        Portals are very useful to simplify your code. I couldn't do without them anymore.



        Note that portal implementations can also help you with other useful features like:




        • Accessibility

        • Espace shortcuts to close the portal

        • Handle outside click (close portal or not)

        • Handle link click (close portal or not)

        • React Context made available in portal tree


        react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.



        react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.



        You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:



        Onboarding hotspot



        Real production code here. Can't be any simpler :)



        <MenuHotspots.contacts>
        <ContactButton/>
        </MenuHotspots.contacts>




        Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)



        Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.



        Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree






        share|improve this answer















        Update: React 16.0 introduced portals through ReactDOM.createPortal link



        Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link





        Use portals



        Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.



        The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.



        What is a portal?



        A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.



        The idea is that for example you render into body the following React tree:



        <div className="layout">
        <div className="outside-portal">
        <Portal>
        <div className="inside-portal">
        PortalContent
        </div>
        </Portal>
        </div>
        </div>


        And you get as output:



        <body>
        <div class="layout">
        <div class="outside-portal">
        </div>
        </div>
        <div class="inside-portal">
        PortalContent
        </div>
        </body>


        The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.



        When to use a portal



        A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots



        Why use a portal



        No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.



        In practice



        Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:



        class DeleteButton extends React.Component {
        static propTypes = {
        onDelete: PropTypes.func.isRequired,
        };

        state = { confirmationPopup: false };

        open = () => {
        this.setState({ confirmationPopup: true });
        };

        close = () => {
        this.setState({ confirmationPopup: false });
        };

        render() {
        return (
        <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
        <Portal>
        <DeleteConfirmationPopup
        onCancel={() => this.close()}
        onConfirm={() => {
        this.close();
        this.props.onDelete();
        }}
        />
        </Portal>
        )}
        </div>
        );
        }
        }


        Simple: you can still use Redux state: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton



        Conclusion



        Portals are very useful to simplify your code. I couldn't do without them anymore.



        Note that portal implementations can also help you with other useful features like:




        • Accessibility

        • Espace shortcuts to close the portal

        • Handle outside click (close portal or not)

        • Handle link click (close portal or not)

        • React Context made available in portal tree


        react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.



        react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.



        You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:



        Onboarding hotspot



        Real production code here. Can't be any simpler :)



        <MenuHotspots.contacts>
        <ContactButton/>
        </MenuHotspots.contacts>




        Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)



        Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.



        Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Sep 19 '18 at 8:09

























        answered Oct 3 '16 at 9:17









        Sebastien LorberSebastien Lorber

        53.3k39206336




        53.3k39206336













        • In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

          – dKab
          Dec 29 '16 at 9:13











        • It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

          – stone
          Mar 15 '17 at 19:54






        • 1





          @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

          – Sebastien Lorber
          Mar 15 '17 at 20:33











        • react-gateway is awesome! It support server side rendering :)

          – cyrilluce
          Jun 17 '17 at 10:21



















        • In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

          – dKab
          Dec 29 '16 at 9:13











        • It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

          – stone
          Mar 15 '17 at 19:54






        • 1





          @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

          – Sebastien Lorber
          Mar 15 '17 at 20:33











        • react-gateway is awesome! It support server side rendering :)

          – cyrilluce
          Jun 17 '17 at 10:21

















        In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

        – dKab
        Dec 29 '16 at 9:13





        In you example snippet the confirmation popup won't close if you confirm the action (opposed to when you click on Cancel)

        – dKab
        Dec 29 '16 at 9:13













        It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

        – stone
        Mar 15 '17 at 19:54





        It would be helpful to include your Portal import in the code snippet. What library does <Portal> come from? I'm guessing it's react-portal, but it'd be nice to know for sure.

        – stone
        Mar 15 '17 at 19:54




        1




        1





        @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

        – Sebastien Lorber
        Mar 15 '17 at 20:33





        @skypecakes please consider my implementations as pseudo-code. I didn't test it against any concrete library. I just try to teach the concept here not a concrete implementation. I'm used to react-portal and code above should work fine with it, but it should work fine with almost any similar lib.

        – Sebastien Lorber
        Mar 15 '17 at 20:33













        react-gateway is awesome! It support server side rendering :)

        – cyrilluce
        Jun 17 '17 at 10:21





        react-gateway is awesome! It support server side rendering :)

        – cyrilluce
        Jun 17 '17 at 10:21











        8














        A lot of good solutions and valuable commentaries by known experts from JS community on the topic could be found here. It could be an indicator that it's not that trivial problem as it may seem. I think this is why it could be the source of doubts and uncertainty on the issue.



        Fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?



        I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale




        Rationale



        react/react-dom comes comes with 2 basic assumptions/ideas:




        • every UI is hierarchical naturally. This why we have the idea of components which wrap each other


        • react-dom mounts (physically) child component to its parent DOM node by default


        The problem is that sometimes the second property isn't what you want
        in your case. Sometimes you want to mount your component into
        different physical DOM node and hold logical connection between
        parent and child at the same time.



        Canonical example is Tooltip-like component: at some point of
        development process you could find that you need to add some
        description for your UI element: it'll render in fixed layer and
        should know its coordinates (which are that UI element coord or
        mouse coords) and at the same time it needs information whether it
        needs to be shown right now or not, its content and some context from
        parent components. This example shows that sometimes logical hierarchy
        isn't match with the physical DOM hierarchy.




        Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question:



        import { Layer, LayerContext } from 'react-layer-stack'
        // ... for each `object` in array of `objects`
        const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
        return (
        <Cell {...props}>
        // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
        <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
        hideMe, // alias for `hide(modalId)`
        index } // useful to know to set zIndex, for example
        , e) => // access to the arguments (click event data in this example)
        <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
        <ConfirmationDialog
        title={ 'Delete' }
        message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
        confirmButton={ <Button type="primary">DELETE</Button> }
        onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
        close={ hideMe } />
        </Modal> }
        </Layer>

        // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
        <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
        <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
        <Icon type="trash" />
        </div> }
        </LayerContext>
        </Cell>)
        // ...





        share|improve this answer






























          8














          A lot of good solutions and valuable commentaries by known experts from JS community on the topic could be found here. It could be an indicator that it's not that trivial problem as it may seem. I think this is why it could be the source of doubts and uncertainty on the issue.



          Fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?



          I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale




          Rationale



          react/react-dom comes comes with 2 basic assumptions/ideas:




          • every UI is hierarchical naturally. This why we have the idea of components which wrap each other


          • react-dom mounts (physically) child component to its parent DOM node by default


          The problem is that sometimes the second property isn't what you want
          in your case. Sometimes you want to mount your component into
          different physical DOM node and hold logical connection between
          parent and child at the same time.



          Canonical example is Tooltip-like component: at some point of
          development process you could find that you need to add some
          description for your UI element: it'll render in fixed layer and
          should know its coordinates (which are that UI element coord or
          mouse coords) and at the same time it needs information whether it
          needs to be shown right now or not, its content and some context from
          parent components. This example shows that sometimes logical hierarchy
          isn't match with the physical DOM hierarchy.




          Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question:



          import { Layer, LayerContext } from 'react-layer-stack'
          // ... for each `object` in array of `objects`
          const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
          return (
          <Cell {...props}>
          // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
          <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
          hideMe, // alias for `hide(modalId)`
          index } // useful to know to set zIndex, for example
          , e) => // access to the arguments (click event data in this example)
          <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
          <ConfirmationDialog
          title={ 'Delete' }
          message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
          confirmButton={ <Button type="primary">DELETE</Button> }
          onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
          close={ hideMe } />
          </Modal> }
          </Layer>

          // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
          <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
          <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
          <Icon type="trash" />
          </div> }
          </LayerContext>
          </Cell>)
          // ...





          share|improve this answer




























            8












            8








            8







            A lot of good solutions and valuable commentaries by known experts from JS community on the topic could be found here. It could be an indicator that it's not that trivial problem as it may seem. I think this is why it could be the source of doubts and uncertainty on the issue.



            Fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?



            I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale




            Rationale



            react/react-dom comes comes with 2 basic assumptions/ideas:




            • every UI is hierarchical naturally. This why we have the idea of components which wrap each other


            • react-dom mounts (physically) child component to its parent DOM node by default


            The problem is that sometimes the second property isn't what you want
            in your case. Sometimes you want to mount your component into
            different physical DOM node and hold logical connection between
            parent and child at the same time.



            Canonical example is Tooltip-like component: at some point of
            development process you could find that you need to add some
            description for your UI element: it'll render in fixed layer and
            should know its coordinates (which are that UI element coord or
            mouse coords) and at the same time it needs information whether it
            needs to be shown right now or not, its content and some context from
            parent components. This example shows that sometimes logical hierarchy
            isn't match with the physical DOM hierarchy.




            Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question:



            import { Layer, LayerContext } from 'react-layer-stack'
            // ... for each `object` in array of `objects`
            const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
            return (
            <Cell {...props}>
            // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
            <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
            <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
            title={ 'Delete' }
            message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
            confirmButton={ <Button type="primary">DELETE</Button> }
            onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
            close={ hideMe } />
            </Modal> }
            </Layer>

            // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
            <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
            <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
            </div> }
            </LayerContext>
            </Cell>)
            // ...





            share|improve this answer















            A lot of good solutions and valuable commentaries by known experts from JS community on the topic could be found here. It could be an indicator that it's not that trivial problem as it may seem. I think this is why it could be the source of doubts and uncertainty on the issue.



            Fundamental problem here is that in React you're only allowed to mount component to its parent, which is not always the desired behavior. But how to address this issue?



            I propose the solution, addressed to fix this issue. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale




            Rationale



            react/react-dom comes comes with 2 basic assumptions/ideas:




            • every UI is hierarchical naturally. This why we have the idea of components which wrap each other


            • react-dom mounts (physically) child component to its parent DOM node by default


            The problem is that sometimes the second property isn't what you want
            in your case. Sometimes you want to mount your component into
            different physical DOM node and hold logical connection between
            parent and child at the same time.



            Canonical example is Tooltip-like component: at some point of
            development process you could find that you need to add some
            description for your UI element: it'll render in fixed layer and
            should know its coordinates (which are that UI element coord or
            mouse coords) and at the same time it needs information whether it
            needs to be shown right now or not, its content and some context from
            parent components. This example shows that sometimes logical hierarchy
            isn't match with the physical DOM hierarchy.




            Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to see the concrete example which is answer to your question:



            import { Layer, LayerContext } from 'react-layer-stack'
            // ... for each `object` in array of `objects`
            const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
            return (
            <Cell {...props}>
            // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
            <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
            <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
            title={ 'Delete' }
            message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
            confirmButton={ <Button type="primary">DELETE</Button> }
            onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
            close={ hideMe } />
            </Modal> }
            </Layer>

            // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
            <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
            <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
            </div> }
            </LayerContext>
            </Cell>)
            // ...






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 8 '16 at 10:37

























            answered Nov 7 '16 at 9:23









            fcktfckt

            303310




            303310























                1














                In my opinion the bare minimum implementation has two requirements. A state that keeps track of whether the modal is open or not, and a portal to render the modal outside of the standard react tree.



                The ModalContainer component below implements those requirements along with corresponding render functions for the modal and the trigger, which is responsible for executing the callback to open the modal.



                import React from 'react';
                import PropTypes from 'prop-types';
                import Portal from 'react-portal';

                class ModalContainer extends React.Component {
                state = {
                isOpen: false,
                };

                openModal = () => {
                this.setState(() => ({ isOpen: true }));
                }

                closeModal = () => {
                this.setState(() => ({ isOpen: false }));
                }

                renderModal() {
                return (
                this.props.renderModal({
                isOpen: this.state.isOpen,
                closeModal: this.closeModal,
                })
                );
                }

                renderTrigger() {
                return (
                this.props.renderTrigger({
                openModal: this.openModal
                })
                )
                }

                render() {
                return (
                <React.Fragment>
                <Portal>
                {this.renderModal()}
                </Portal>
                {this.renderTrigger()}
                </React.Fragment>
                );
                }
                }

                ModalContainer.propTypes = {
                renderModal: PropTypes.func.isRequired,
                renderTrigger: PropTypes.func.isRequired,
                };

                export default ModalContainer;


                And here's a simple use case...



                import React from 'react';
                import Modal from 'react-modal';
                import Fade from 'components/Animations/Fade';
                import ModalContainer from 'components/ModalContainer';

                const SimpleModal = ({ isOpen, closeModal }) => (
                <Fade visible={isOpen}> // example use case with animation components
                <Modal>
                <Button onClick={closeModal}>
                close modal
                </Button>
                </Modal>
                </Fade>
                );

                const SimpleModalButton = ({ openModal }) => (
                <button onClick={openModal}>
                open modal
                </button>
                );

                const SimpleButtonWithModal = () => (
                <ModalContainer
                renderModal={props => <SimpleModal {...props} />}
                renderTrigger={props => <SimpleModalButton {...props} />}
                />
                );

                export default SimpleButtonWithModal;


                I use render functions, because I want to isolate state management and boilerplate logic from the implementation of the rendered modal and trigger component. This allows the rendered components to be whatever you want them to be. In your case, I assume the modal component would be a connected component that receives a callback function that dispatches an asynchronous action.



                If you need to send dynamic props to the modal component from the trigger component, which hopefully doesn't happen too often, I recommend wrapping the ModalContainer with a container component that manages the dynamic props in its own state and enhance the original render methods like so.



                import React from 'react'
                import partialRight from 'lodash/partialRight';
                import ModalContainer from 'components/ModalContainer';

                class ErrorModalContainer extends React.Component {
                state = { message: '' }

                onError = (message, callback) => {
                this.setState(
                () => ({ message }),
                () => callback && callback()
                );
                }

                renderModal = (props) => (
                this.props.renderModal({
                ...props,
                message: this.state.message,
                })
                )

                renderTrigger = (props) => (
                this.props.renderTrigger({
                openModal: partialRight(this.onError, props.openModal)
                })
                )

                render() {
                return (
                <ModalContainer
                renderModal={this.renderModal}
                renderTrigger={this.renderTrigger}
                />
                )
                }
                }

                ErrorModalContainer.propTypes = (
                ModalContainer.propTypes
                );

                export default ErrorModalContainer;





                share|improve this answer






























                  1














                  In my opinion the bare minimum implementation has two requirements. A state that keeps track of whether the modal is open or not, and a portal to render the modal outside of the standard react tree.



                  The ModalContainer component below implements those requirements along with corresponding render functions for the modal and the trigger, which is responsible for executing the callback to open the modal.



                  import React from 'react';
                  import PropTypes from 'prop-types';
                  import Portal from 'react-portal';

                  class ModalContainer extends React.Component {
                  state = {
                  isOpen: false,
                  };

                  openModal = () => {
                  this.setState(() => ({ isOpen: true }));
                  }

                  closeModal = () => {
                  this.setState(() => ({ isOpen: false }));
                  }

                  renderModal() {
                  return (
                  this.props.renderModal({
                  isOpen: this.state.isOpen,
                  closeModal: this.closeModal,
                  })
                  );
                  }

                  renderTrigger() {
                  return (
                  this.props.renderTrigger({
                  openModal: this.openModal
                  })
                  )
                  }

                  render() {
                  return (
                  <React.Fragment>
                  <Portal>
                  {this.renderModal()}
                  </Portal>
                  {this.renderTrigger()}
                  </React.Fragment>
                  );
                  }
                  }

                  ModalContainer.propTypes = {
                  renderModal: PropTypes.func.isRequired,
                  renderTrigger: PropTypes.func.isRequired,
                  };

                  export default ModalContainer;


                  And here's a simple use case...



                  import React from 'react';
                  import Modal from 'react-modal';
                  import Fade from 'components/Animations/Fade';
                  import ModalContainer from 'components/ModalContainer';

                  const SimpleModal = ({ isOpen, closeModal }) => (
                  <Fade visible={isOpen}> // example use case with animation components
                  <Modal>
                  <Button onClick={closeModal}>
                  close modal
                  </Button>
                  </Modal>
                  </Fade>
                  );

                  const SimpleModalButton = ({ openModal }) => (
                  <button onClick={openModal}>
                  open modal
                  </button>
                  );

                  const SimpleButtonWithModal = () => (
                  <ModalContainer
                  renderModal={props => <SimpleModal {...props} />}
                  renderTrigger={props => <SimpleModalButton {...props} />}
                  />
                  );

                  export default SimpleButtonWithModal;


                  I use render functions, because I want to isolate state management and boilerplate logic from the implementation of the rendered modal and trigger component. This allows the rendered components to be whatever you want them to be. In your case, I assume the modal component would be a connected component that receives a callback function that dispatches an asynchronous action.



                  If you need to send dynamic props to the modal component from the trigger component, which hopefully doesn't happen too often, I recommend wrapping the ModalContainer with a container component that manages the dynamic props in its own state and enhance the original render methods like so.



                  import React from 'react'
                  import partialRight from 'lodash/partialRight';
                  import ModalContainer from 'components/ModalContainer';

                  class ErrorModalContainer extends React.Component {
                  state = { message: '' }

                  onError = (message, callback) => {
                  this.setState(
                  () => ({ message }),
                  () => callback && callback()
                  );
                  }

                  renderModal = (props) => (
                  this.props.renderModal({
                  ...props,
                  message: this.state.message,
                  })
                  )

                  renderTrigger = (props) => (
                  this.props.renderTrigger({
                  openModal: partialRight(this.onError, props.openModal)
                  })
                  )

                  render() {
                  return (
                  <ModalContainer
                  renderModal={this.renderModal}
                  renderTrigger={this.renderTrigger}
                  />
                  )
                  }
                  }

                  ErrorModalContainer.propTypes = (
                  ModalContainer.propTypes
                  );

                  export default ErrorModalContainer;





                  share|improve this answer




























                    1












                    1








                    1







                    In my opinion the bare minimum implementation has two requirements. A state that keeps track of whether the modal is open or not, and a portal to render the modal outside of the standard react tree.



                    The ModalContainer component below implements those requirements along with corresponding render functions for the modal and the trigger, which is responsible for executing the callback to open the modal.



                    import React from 'react';
                    import PropTypes from 'prop-types';
                    import Portal from 'react-portal';

                    class ModalContainer extends React.Component {
                    state = {
                    isOpen: false,
                    };

                    openModal = () => {
                    this.setState(() => ({ isOpen: true }));
                    }

                    closeModal = () => {
                    this.setState(() => ({ isOpen: false }));
                    }

                    renderModal() {
                    return (
                    this.props.renderModal({
                    isOpen: this.state.isOpen,
                    closeModal: this.closeModal,
                    })
                    );
                    }

                    renderTrigger() {
                    return (
                    this.props.renderTrigger({
                    openModal: this.openModal
                    })
                    )
                    }

                    render() {
                    return (
                    <React.Fragment>
                    <Portal>
                    {this.renderModal()}
                    </Portal>
                    {this.renderTrigger()}
                    </React.Fragment>
                    );
                    }
                    }

                    ModalContainer.propTypes = {
                    renderModal: PropTypes.func.isRequired,
                    renderTrigger: PropTypes.func.isRequired,
                    };

                    export default ModalContainer;


                    And here's a simple use case...



                    import React from 'react';
                    import Modal from 'react-modal';
                    import Fade from 'components/Animations/Fade';
                    import ModalContainer from 'components/ModalContainer';

                    const SimpleModal = ({ isOpen, closeModal }) => (
                    <Fade visible={isOpen}> // example use case with animation components
                    <Modal>
                    <Button onClick={closeModal}>
                    close modal
                    </Button>
                    </Modal>
                    </Fade>
                    );

                    const SimpleModalButton = ({ openModal }) => (
                    <button onClick={openModal}>
                    open modal
                    </button>
                    );

                    const SimpleButtonWithModal = () => (
                    <ModalContainer
                    renderModal={props => <SimpleModal {...props} />}
                    renderTrigger={props => <SimpleModalButton {...props} />}
                    />
                    );

                    export default SimpleButtonWithModal;


                    I use render functions, because I want to isolate state management and boilerplate logic from the implementation of the rendered modal and trigger component. This allows the rendered components to be whatever you want them to be. In your case, I assume the modal component would be a connected component that receives a callback function that dispatches an asynchronous action.



                    If you need to send dynamic props to the modal component from the trigger component, which hopefully doesn't happen too often, I recommend wrapping the ModalContainer with a container component that manages the dynamic props in its own state and enhance the original render methods like so.



                    import React from 'react'
                    import partialRight from 'lodash/partialRight';
                    import ModalContainer from 'components/ModalContainer';

                    class ErrorModalContainer extends React.Component {
                    state = { message: '' }

                    onError = (message, callback) => {
                    this.setState(
                    () => ({ message }),
                    () => callback && callback()
                    );
                    }

                    renderModal = (props) => (
                    this.props.renderModal({
                    ...props,
                    message: this.state.message,
                    })
                    )

                    renderTrigger = (props) => (
                    this.props.renderTrigger({
                    openModal: partialRight(this.onError, props.openModal)
                    })
                    )

                    render() {
                    return (
                    <ModalContainer
                    renderModal={this.renderModal}
                    renderTrigger={this.renderTrigger}
                    />
                    )
                    }
                    }

                    ErrorModalContainer.propTypes = (
                    ModalContainer.propTypes
                    );

                    export default ErrorModalContainer;





                    share|improve this answer















                    In my opinion the bare minimum implementation has two requirements. A state that keeps track of whether the modal is open or not, and a portal to render the modal outside of the standard react tree.



                    The ModalContainer component below implements those requirements along with corresponding render functions for the modal and the trigger, which is responsible for executing the callback to open the modal.



                    import React from 'react';
                    import PropTypes from 'prop-types';
                    import Portal from 'react-portal';

                    class ModalContainer extends React.Component {
                    state = {
                    isOpen: false,
                    };

                    openModal = () => {
                    this.setState(() => ({ isOpen: true }));
                    }

                    closeModal = () => {
                    this.setState(() => ({ isOpen: false }));
                    }

                    renderModal() {
                    return (
                    this.props.renderModal({
                    isOpen: this.state.isOpen,
                    closeModal: this.closeModal,
                    })
                    );
                    }

                    renderTrigger() {
                    return (
                    this.props.renderTrigger({
                    openModal: this.openModal
                    })
                    )
                    }

                    render() {
                    return (
                    <React.Fragment>
                    <Portal>
                    {this.renderModal()}
                    </Portal>
                    {this.renderTrigger()}
                    </React.Fragment>
                    );
                    }
                    }

                    ModalContainer.propTypes = {
                    renderModal: PropTypes.func.isRequired,
                    renderTrigger: PropTypes.func.isRequired,
                    };

                    export default ModalContainer;


                    And here's a simple use case...



                    import React from 'react';
                    import Modal from 'react-modal';
                    import Fade from 'components/Animations/Fade';
                    import ModalContainer from 'components/ModalContainer';

                    const SimpleModal = ({ isOpen, closeModal }) => (
                    <Fade visible={isOpen}> // example use case with animation components
                    <Modal>
                    <Button onClick={closeModal}>
                    close modal
                    </Button>
                    </Modal>
                    </Fade>
                    );

                    const SimpleModalButton = ({ openModal }) => (
                    <button onClick={openModal}>
                    open modal
                    </button>
                    );

                    const SimpleButtonWithModal = () => (
                    <ModalContainer
                    renderModal={props => <SimpleModal {...props} />}
                    renderTrigger={props => <SimpleModalButton {...props} />}
                    />
                    );

                    export default SimpleButtonWithModal;


                    I use render functions, because I want to isolate state management and boilerplate logic from the implementation of the rendered modal and trigger component. This allows the rendered components to be whatever you want them to be. In your case, I assume the modal component would be a connected component that receives a callback function that dispatches an asynchronous action.



                    If you need to send dynamic props to the modal component from the trigger component, which hopefully doesn't happen too often, I recommend wrapping the ModalContainer with a container component that manages the dynamic props in its own state and enhance the original render methods like so.



                    import React from 'react'
                    import partialRight from 'lodash/partialRight';
                    import ModalContainer from 'components/ModalContainer';

                    class ErrorModalContainer extends React.Component {
                    state = { message: '' }

                    onError = (message, callback) => {
                    this.setState(
                    () => ({ message }),
                    () => callback && callback()
                    );
                    }

                    renderModal = (props) => (
                    this.props.renderModal({
                    ...props,
                    message: this.state.message,
                    })
                    )

                    renderTrigger = (props) => (
                    this.props.renderTrigger({
                    openModal: partialRight(this.onError, props.openModal)
                    })
                    )

                    render() {
                    return (
                    <ModalContainer
                    renderModal={this.renderModal}
                    renderTrigger={this.renderTrigger}
                    />
                    )
                    }
                    }

                    ErrorModalContainer.propTypes = (
                    ModalContainer.propTypes
                    );

                    export default ErrorModalContainer;






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Jan 17 at 7:40

























                    answered Nov 28 '18 at 3:49









                    kskkidokskkido

                    112




                    112























                        0














                        Wrap the modal into a connected container and perform the async operation in here. This way you can reach both the dispatch to trigger actions, and the onClose prop too. (To reach dispatch from props, do not passs mapDispatchToProps function to connect.



                        class ModalConteiner extends React.Component {
                        handleDelete = () => {
                        const { dispatch, onClose } = this.props;
                        dispatch({type: 'DELETE_POST'});

                        someAsyncOperation().then(() => {
                        dispatch({type: 'DELETE_POST_SUCCESS'});
                        onClose();
                        })
                        }

                        render() {
                        const { onClose } = this.props;
                        return <Modal onClose={onClose} onSubmit={this.handleDelete} />
                        }
                        }

                        export default connect(/* no map dispatch to props here! */)(ModalContainer);




                        The app where the modal is rendered and its visibility state is set:



                        class App extends React.Component {
                        state = {
                        isModalOpen: false
                        }

                        handleModalClose = () => this.setState({ isModalOpen: false });

                        ...

                        render(){
                        return (
                        ...
                        <ModalContainer onClose={this.handleModalClose} />
                        ...
                        )
                        }

                        }







                        share|improve this answer




























                          0














                          Wrap the modal into a connected container and perform the async operation in here. This way you can reach both the dispatch to trigger actions, and the onClose prop too. (To reach dispatch from props, do not passs mapDispatchToProps function to connect.



                          class ModalConteiner extends React.Component {
                          handleDelete = () => {
                          const { dispatch, onClose } = this.props;
                          dispatch({type: 'DELETE_POST'});

                          someAsyncOperation().then(() => {
                          dispatch({type: 'DELETE_POST_SUCCESS'});
                          onClose();
                          })
                          }

                          render() {
                          const { onClose } = this.props;
                          return <Modal onClose={onClose} onSubmit={this.handleDelete} />
                          }
                          }

                          export default connect(/* no map dispatch to props here! */)(ModalContainer);




                          The app where the modal is rendered and its visibility state is set:



                          class App extends React.Component {
                          state = {
                          isModalOpen: false
                          }

                          handleModalClose = () => this.setState({ isModalOpen: false });

                          ...

                          render(){
                          return (
                          ...
                          <ModalContainer onClose={this.handleModalClose} />
                          ...
                          )
                          }

                          }







                          share|improve this answer


























                            0












                            0








                            0







                            Wrap the modal into a connected container and perform the async operation in here. This way you can reach both the dispatch to trigger actions, and the onClose prop too. (To reach dispatch from props, do not passs mapDispatchToProps function to connect.



                            class ModalConteiner extends React.Component {
                            handleDelete = () => {
                            const { dispatch, onClose } = this.props;
                            dispatch({type: 'DELETE_POST'});

                            someAsyncOperation().then(() => {
                            dispatch({type: 'DELETE_POST_SUCCESS'});
                            onClose();
                            })
                            }

                            render() {
                            const { onClose } = this.props;
                            return <Modal onClose={onClose} onSubmit={this.handleDelete} />
                            }
                            }

                            export default connect(/* no map dispatch to props here! */)(ModalContainer);




                            The app where the modal is rendered and its visibility state is set:



                            class App extends React.Component {
                            state = {
                            isModalOpen: false
                            }

                            handleModalClose = () => this.setState({ isModalOpen: false });

                            ...

                            render(){
                            return (
                            ...
                            <ModalContainer onClose={this.handleModalClose} />
                            ...
                            )
                            }

                            }







                            share|improve this answer













                            Wrap the modal into a connected container and perform the async operation in here. This way you can reach both the dispatch to trigger actions, and the onClose prop too. (To reach dispatch from props, do not passs mapDispatchToProps function to connect.



                            class ModalConteiner extends React.Component {
                            handleDelete = () => {
                            const { dispatch, onClose } = this.props;
                            dispatch({type: 'DELETE_POST'});

                            someAsyncOperation().then(() => {
                            dispatch({type: 'DELETE_POST_SUCCESS'});
                            onClose();
                            })
                            }

                            render() {
                            const { onClose } = this.props;
                            return <Modal onClose={onClose} onSubmit={this.handleDelete} />
                            }
                            }

                            export default connect(/* no map dispatch to props here! */)(ModalContainer);




                            The app where the modal is rendered and its visibility state is set:



                            class App extends React.Component {
                            state = {
                            isModalOpen: false
                            }

                            handleModalClose = () => this.setState({ isModalOpen: false });

                            ...

                            render(){
                            return (
                            ...
                            <ModalContainer onClose={this.handleModalClose} />
                            ...
                            )
                            }

                            }








                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Jan 24 at 14:30









                            gazdagergogazdagergo

                            1,410718




                            1,410718






























                                draft saved

                                draft discarded




















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid



                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.


                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function () {
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f35623656%2fhow-can-i-display-a-modal-dialog-in-redux-that-performs-asynchronous-actions%23new-answer', 'question_page');
                                }
                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown







                                Popular posts from this blog

                                A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                                Calculate evaluation metrics using cross_val_predict sklearn

                                Insert data from modal to MySQL (multiple modal on website)