问题
I have a problem with the concept of modals in React. When using server side rendered templates with jQuery I was used to have one empty global modal template always available (included in base template that was always extended). Then when making AJAX call I just populated modal..something like this:
$('.modal-global-content').html(content);
$('.modal-global').show();
So how do I make this concept in React?
回答1:
There are a few ways of doing this. The first involves passing in the modal state from a parent component. Here's how to do this - first with the parent App.js
component:
// App.js
import React from "react";
import Modal from "./Modal";
const App = () => {
const [showModal, updateShowModal] = React.useState(false);
const toggleModal = () => updateShowModal(state => !state);
return (
<div>
<h1>Not a modal</h1>
<button onClick={toggleModal}>Show Modal</button>
<Modal canShow={showModal} updateModalState={toggleModal} />
</div>
);
}
export default App;
And here's the Modal.js
child component that will render the modal:
// Modal.js
import React from "react";
const modalStyles = {
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
background: "blue"
};
const Modal = ({ canShow, updateModalState }) => {
if (canShow) {
return (
<div style={modalStyles}>
<h1>I'm a Modal!</h1>
<button onClick={updateModalState}>Hide Me</button>
</div>
);
}
return null;
};
export default Modal;
This way is perfectly fine, but it can get a bit repetitive if you're reusing the modal in many places throughout your app. So instead, I would recommend using the context API.
Define a context object for your modal state, create a provider near the top of your application, then whenever you have a child component that needs to render the modal, you can render a consumer of the modal context. This way you can easily nest your modal deeper in your component tree without having to pass callbacks all the way down. Here's how to do this - first by creating a context.js
file:
// context.js
import React from "react";
export const ModalContext = React.createContext();
Now the updated App.js
file:
// App.js
import React from "react";
import { ModalContext } from "./context";
import Modal from "./Modal";
const App = () => {
const [showModal, updateShowModal] = React.useState(false);
const toggleModal = () => updateShowModal(state => !state);
return (
<ModalContext.Provider value={{ showModal, toggleModal }}>
<div>
<h1>Not a modal</h1>
<button onClick={toggleModal}>Show Modal</button>
<Modal canShow={showModal} updateModalState={toggleModal} />
</div>
</ModalContext.Provider>
);
}
export default App;
And lastly the updated Modal.js
file:
// Modal.js
import React from "react";
import { ModalContext } from "./context";
const modalStyles = {
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
background: "blue"
};
const Modal = () => {
return (
<ModalContext.Consumer>
{context => {
if (context.showModal) {
return (
<div style={modalStyles}>
<h1>I'm a Modal!</h1>
<button onClick={context.toggleModal}>Hide Me</button>
</div>
);
}
return null;
}}
</ModalContext.Consumer>
);
};
export default Modal;
Here's a Codesandbox link with a working version using context. I hope this helps!
回答2:
One way you can solve this problem by using css and JSX.
this is the app and i can have anything like a button a link anything
Lets assume we have a link (react-router-dom)
which redirects us to
a DeletePage
The Delete Page renders a Modal
You will provide the title
and the actions
of the Modal as props
const App = () => {
return(
<Link to="/something/someid">SomeAction</Link>
)
}
const DeletePage = () => {
return(
<Modal
title="Are you sure you want to delete this"
dismiss={() => history.replace("/")}
action={() => console.log("deleted") }
/>
)
}
Modal
const Modal = (props) => {
return(
<div>
<div className="background" onClick={props.dismiss}/>
<h1>{props.title}</h1>
<button onClick={props.dismiss}>Cancel</button>
<button onClick={props.action}>Delete</button>
</div>
)
}
- set the
z-index
of the modal a high number position: fixed
of the modal component- when the user will click on the background the model will go away ( many ways to implement that like with modal state, redirect, etc i have taken the redirect as one of the ways )
cancel button
also has the same onClick function which is to dismissDelete button
has theaction
function passed through props
this method has a flaw because of css because if your parent component
has a position property of relative
then this will break.
The modal will remain inside the parent no matter how high the z-index is
To Save us here comes React-Portal
React portal creates a 'portal' in its own way
The react code you might have will render inside DOM
with id of #root
( in most cases )
So to render our Modal
as the top most layer we create another
DOM element
eg <div id="modal"></div>
in the public index.html
file
The Modal
react component code will slightly change
const Modal = (props) => {
return ReactDOM.createPortal(
<div>
<div className="background" onClick={props.dismiss}/>
<h1>{props.title}</h1>
<button onClick={props.dismiss}>Cancel</button>
<button onClick={props.action}>Delete</button>
</div>
),document.querySelector("#modal")
}
rest is all the same
来源:https://stackoverflow.com/questions/62502954/how-to-create-reusable-custom-modal-component-in-react