问题
I'm having a lot of problems trying to figure out how to add a functioning close button to my modal - constructor/props hasn't been working and I'm not sure what to put after onClick= in the button element.
class Modal extends React.Component {
// whenever component gets rendered to the screen we create a new div assigned to this.modalTarget
componentDidMount() {
this.modalTarget = document.createElement('div');
// add class name to modal target
this.modalTarget.className = 'modal';
// take the div we just created and append it to the body tag in doc
document.body.appendChild(this.modalTarget);
// call method _render
this._render();
}
// whenever the component's about to update we do another render
// this render makes sure that if we get a new set of components or children in the modal
// we're going to render those to the parent div as well
componentWillUpdate() {
this._render();
}
// clean up - whenever the component is about to unmount from the screen
// cleans up dom when the modal is removed from the component heirarchy
componentWillUnmount() {
// unmounts this.props.children
ReactDOM.unmountComponentAtNode(this.modalTarget);
document.body.removeChild(this.modalTarget);
}
_render() {
// take react dom library and render a div that contains this.props.children
// and render it into this.modalTarget
ReactDOM.render(
<Provider store= {store}>
<Router>
<div className="modal">
{this.props.children}
<button>Close</button>
</div>
</Router>
</Provider>,
this.modalTarget
回答1:
Several issues here. First, move away from directly manipulating the DOM
. React uses a virtual DOM
, so you don't need to manually add or remove DOM
elements. React automatically handles this DOM
manipulation through the render
method.
Also, you'll need to control this Modal
utilizing some sort of state
(isOpen). It be can through React's local state or through Redux's state. Either way, it needs to be controlled and compared against. Put simply, if it's open, render the Modal
, if it's closed, render null
.
In addition, this Modal
component can be structured to be reusable. Simply add it as a child
to another stateful parent
component and include whatever children
you want to render inside of it.
Working example:
components/Example.js (parent component)
import React, { Component } from "react";
import Modal from "../Modal";
import "./styles.css";
class Example extends Component {
state = { isOpen: false };
handleOpenModal = () => {
this.setState({ isOpen: true });
};
handleCloseModal = () => {
this.setState({ isOpen: false });
};
render = () => (
<div className="example">
<h2>Simple Modal Example</h2>
<button
className="uk-button uk-button-primary uk-button-small"
onClick={this.handleOpenModal}
>
Open Modal
</button>
<Modal isOpen={this.state.isOpen} onCloseModal={this.handleCloseModal}>
<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.handleCloseModal}
>
Close
</button>
</Modal>
</div>
);
}
export default Example;
components/Modal.js (child component -- this has a lot of smaller components that were separated for reusability and ease of understanding -- they're basically simple div
s with some styles
attached -- see notes below)
import React from "react";
import PropTypes from "prop-types";
import BackgroundOverlay from "../BackgroundOverlay"; // grey background
import ClickHandler from "../ClickHandler"; // handles clicks outside of the modal
import Container from "../Container"; // contains the modal and background
import Content from "../Content"; // renders the "children" placed inside of <Modal>...</Modal>
import ModalContainer from "../ModalContainer"; // places the modal in the center of the page
// this is a ternary operator (shorthand for "if/else" -- if cond ? then : else)
// below can be read like: if isOpen is true, then return/render the modal, else return null
const Modal = ({ children, isOpen, onCloseModal }) =>
isOpen ? (
<Container>
<BackgroundOverlay />
<ModalContainer>
<ClickHandler isOpen={isOpen} closeModal={onCloseModal}>
<Content>{children}</Content>
</ClickHandler>
</ModalContainer>
</Container>
) : null;
// these proptype declarations are to ensure that passed down props are
// consistent and are defined as expected
Modal.propTypes = {
children: PropTypes.node.isRequired, // children must be a React node
isOpen: PropTypes.bool.isRequired, // isOpen must be a boolean
onCloseModal: PropTypes.func.isRequired // onCloseModal must be a function
};
export default Modal;
回答2:
It looks like your modal is open based simply on whether it's rendered by the parent. Short of all together re-architecting this pattern, the only way to achieve what you want would be to pass in some kind of onClose callback:
class Parent extends React.Component {
state = { isModalOpen: false };
render() {
return (<div>
// Stuff
{this.state.isModalOpen &&
<Modal onClose={() => this.setState({ isModalOpen: false })}/>
}
// Stuff
</div>);
}
}
In your Modal
:
<div className="modal">
{this.props.children}
<button onClick={this.props.onClose}>Close</button>
</div>
来源:https://stackoverflow.com/questions/55836921/close-react-modal