score:6
Accepted answer
Since the click listener will be applied to all modal instances, I'd move the listener and the ref to a reuseable modal component -- now it'll control clicks once opened. The only thing it really needs is a passed down toggle modal function prop from the parent.
Working example:
components/Modal/index.js
import React, { Fragment, PureComponent } from "react";
import { createPortal } from "react-dom";
import PropTypes from "prop-types";
class Modal extends PureComponent {
componentDidMount() {
document.addEventListener("click", this.closeModal, false);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeModal, false);
}
closeModal = ({ target }) => {
if (this.modal && !this.modal.contains(target)) {
this.props.toggleModal();
}
};
render = () =>
createPortal(
<Fragment>
<div className="overlay" />
<div className="window-container">
<div className="modal-container">
<div ref={node => (this.modal = node)} className="modal">
{this.props.children}
</div>
</div>
</div>
</Fragment>,
document.body
);
}
Modal.propTypes = {
children: PropTypes.node.isRequired,
toggleModal: PropTypes.func.isRequired
};
export default Modal;
components/Parent/index.js
import React, { Component } from "react";
import Modal from "../Modal";
class Parent extends Component {
state = {
showModal: false
};
toggleModal = () => {
this.setState(prevState => ({
showModal: !prevState.showModal
}));
};
render = () => (
<div className={`${this.state.showModal ? "blur" : undefined} app`}>
<img
src="https://i.imgur.com/BGwgr3A.jpg"
className="image"
alt="example.png"
onClick={this.toggleModal}
/>
{this.state.showModal && (
<Modal toggleModal={this.toggleModal}>
<h1 className="title">Hello!</h1>
<p className="subtitle">There are two ways to close this modal</p>
<ul>
<li>Click outside of this modal in the grey overlay area.</li>
<li>Click the close button below.</li>
</ul>
<button
className="uk-button uk-button-danger uk-button-small"
onClick={this.toggleModal}
>
Close
</button>
</Modal>
)}
</div>
);
}
export default Parent;
styles.css
.app {
text-align: center;
margin-top: 20px;
}
.blur > img {
-webkit-filter: blur(10px);
-moz-filter: blur(10px);
-ms-filter: blur(10px);
-o-filter: blur(10px);
filter: blur(10px);
}
.image {
display: block;
margin: 0 auto;
cursor: pointer;
width: 600px;
}
.modal {
max-width: 600px;
max-height: calc(100% - 96px);
padding: 20px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
position: relative;
overflow-y: auto;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
box-shadow: 0px 11px 15px -7px rgba(0, 0, 0, 0.2),
0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12);
border-radius: 4px;
background-color: #fff;
text-align: left;
}
.modal-container {
opacity: 1;
-webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
-o-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
height: 100%;
outline: none;
}
.overlay {
opacity: 1;
-webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
-o-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
position: fixed;
-ms-touch-action: none;
touch-action: none;
background-color: rgba(0, 0, 0, 0.5);
-webkit-tap-highlight-color: transparent;
}
.subtitle {
margin: 0;
text-align: center;
font-weight: bold;
}
.title {
text-align: center;
}
.window-container {
text-align: center;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow: auto;
outline: 0;
-webkit-animation: fadeIn 0.2s 0s ease-in-out forwards;
animation: fadeIn 0.2s 0s ease-in-out forwards;
z-index: 100;
}
Source: stackoverflow.com
Related Query
- react Portal - how to close modal when clicking outside of modal and how to assign ref to modal inside parent component rendering modal?
- How to add transition on closing Modal in React and how to close when clicked outside of the Modal?
- How to close a modal window clicking outside in React + Redux app?
- React bootstrap modal how to send post request first and then close modal when submitting values
- Handle outside click closes on clicking the modal itself. Basically shouldn't close when clicked anywhere but outside of the modal
- How to change URLs (but not entire view) when clicking on a modal with Material-ui and react-router 4?
- How can I close a Material UI Modal using an X icon rather than just by clicking outside the modal?
- How to close a modal form when it's sent and it changes the app's state in React?
- How can I close my hamburger menu when clicking on a link using React JS? (using ReactJS hooks)
- React react-bootstrap - How do I close a modal window from an outside component
- React - how to open modal when user tries to reload or close window/tab?
- Bootstrap modal didn't close while clicking outside of modal in React
- Close menu when clicking outside the React component
- How to open React Material-UI modal and populate with Ajax call upon clicking button?
- Will the background component unmount when modal is open in react? If not, how to maintain/store states of modal and react component using redux?
- How to close modal when navigating to another page in React
- How to open pop up modal when form submit sucessfully and hide pop after 2 mintues and redirect to another page in react js?
- You should not use Route or withRouter() outside a Router when using react-router 4 and styled-component in react
- How to prevent child component from re-rendering when using React hooks and memo?
- How to close the modal in react native
- How to close a React Navigation modal with multiple screens in it
- How to mock functions, and test that they're called, when passed as props in react components?
- Closing React Semantic UI modal with button and close icon
- When and how to choose between React Hooks and old HOC props passing?
- React Navigation: How to update navigation title for parent, when value comes from redux and gets updated in child?
- CDN links for React packages and how to import it when using react using the scripts from CDN
- how to access vairables outside of map function in js and jsx in a React component
- How to create a preview (Image and description) to the Url when shared link on Social media,Gmail and Skype using React JS?
- How can I clear the localstorage when user close react application window?
- When and how are React lifecycle methods called in the NextJS SSR process?
More Query from same tag
- Is this.state current when the setState updater is called?
- React not rendering component within if block
- Update a react component only on button click with hooks
- How to implement a self contained component in react redux?
- Have React component trigger a change event the way DOM components would
- React: Generate array of objects before render to avoid changing an uncontrolled input error
- How to redirect / path into /app in react router 6?
- antd InputNumber: execute function onChangeComplete instead of onChange
- Firebase RecaptchaVerifier.clear() has no effect
- Button Event Type Error in React Typescript
- How to fix redirection problems in a react-material menu
- How to edit row name while using ag-grid react
- How can I use regular expressions in react js so that I can you use that in firestore collection name
- React img onError not firing
- Update a value in the object in an Array in react in Redux
- How to not overwrite dispatch with blank state
- Gatsby JS - gatsby-plugin-scroll-reveal not activating
- In which situations can updating the state without callback cause issues?
- React.JS: API called with stale value
- React Key uniqueness on different elements
- Why is Error boundary in React JS not working
- Toggle a class only to one element at each click of the mouse - React hooks
- How to instantiate react component with injected properties
- how to change this state with radio buttons
- Handling Data Objects in ReactElements
- Use of spread operator in useEffect in ReactJS
- ReactJS: Create a select after a find from another list
- Can I hide remove icon only for some files in antd Upload component
- Why do semicolons throw error in react JSX?
- Issue connecting styles.scss in React App