Search code examples
reactjsreact-modal

Any way to open a modal dialog without explicitly storing state?


I have been using react-modal recently, and the pattern to show a modal is something like:

const [modalIsOpen, setModalIsOpen] = useState(false);
return (<ModalDialog isOpen={modalIsOpen}><div>some content</div></ModalDialog>)

...which feels very React-y, but overly verbose. Is there a way to enable something where I can - within a click handler - simply call:

showModal(MyComponent)

and have the state be handled automatically? I've thought about creating a Context around the app that allows showModal to grab state associated with MyComponent, in which case this would itself be a hook, so it would have to follow certain rules (i.e., no conditional calling), so I'm not sure I could make it work as cleanly as I'd like.

Is there a way to do this within the React ecosystem? Or should I just give up and use the existing mechanism?


Solution

  • Yes! You can do it by using the "render" function.

    Unfortunately it doesn't work in CodeSandBox, but you can fork it from here: https://github.com/BHVampire/modal

    You close the modal by clicking outside.

    This is how I did it:

    At the end you'll be able to open a modal like this from any part of your application:

    createModal(<h1>Success</h1>)
    

    First, you add a container for your modals outside the App component, so you'll never have problems with the z-index:

    index.js

    import React from 'react'
    import ReactDOM from 'react-dom'
    import './index.scss'
    import App from './App.jsx'
    
    ReactDOM.render(
      <React.StrictMode>
        <div id="modal-container"></div> <- This is the container
        <App />
      </React.StrictMode>,
      document.getElementById('root')
    )
    

    app.jsx

    import createModal from "./components/Modal"
    
    const App = () => {
      return <button onClick={() => createModal(<h1>Success</h1>)}>Open Modal</button>
    }
    
    export default App
    

    Then I have 2 main files for the modal inside a Modal folder + the style.

    Modal/index.js

    The state hook is unavoidable for the modal, but you'll never see it again.

    import { Fragment, useState } from 'react'
    import './Modal.scss'
    
    const Modal = ({ content }) => {
        const [isOpen, setIsOpen] = useState(true)
    
        return isOpen
            ? <Fragment>
                <div onClick={() => setIsOpen(false)} className="modal-background" />
    
                <div className="modal" >
                    {content}
                </div>
            </Fragment>
            : ''
    }
    
    export default Modal