2

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?

Rollie
  • 4,391
  • 3
  • 33
  • 55
  • Important thing here is, within a single page, you will both want to show and hide a modal. You can write a function which returns the Modal's JSX Component. But still, when you want to close it, you still need some conditional rendering, which is exactly why we use the state. – edison16029 Sep 23 '21 at 01:53
  • Only states have memory in a component but general variables do not, so states need to be used directly or implemented in function with condition to hide/show modal – Jumper Sep 23 '21 at 01:55
  • @edison16029 what I had envisioned was that `MyComponent` have a param like `closeSelf()`. So `showModal` would get a state from shared dialog context, set it to 'open', pass the close function into the sub-component. Then the outer code using all this wouldn't need to do the extra setup. But because this would use context hooks, I think this can't be part of a normal `onClick` handler... – Rollie Sep 23 '21 at 01:59

1 Answers1

1

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
  • One quick follow-up if you know - it seems the rendered dialog can't access context via `useContext`. I assume this is somehow related to the use of `render`, but not sure how it could be corrected. Is there such a way? Or would I need to pass any context data directly into the component? – Rollie Sep 25 '21 at 03:07