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?
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