I'm working on A React project with GraphQl back-end. I have a modal where a user can view more details about a subject. In that modal you can click delete which opens a new modal where you need to confirm if you wan't to delete. When yes is pressed it should be deleted and the modal should close. When no is pressed the modal should just close. The deleting of the subject works. If I press yes is deletes it but the modal doesn't close and when I press no the modal also doesn't close. Can anyone explain why and how I can fix this?
parent modal:
class CalenderModal extends React.Component {
constructor(props) {
super(props);
this.state = {
openDeleteAppointment: false,
};
this.openDeleteAppointment = this.openDeleteAppointment.bind(this);
}
handleRemove = () => {
this.props.onRemove();
}
openDeleteAppointment() {
this.setState({
openDeleteAppointment: true,
})
}
render() {
return (
<React.Fragment>
<div className="customModal">
<div className="modal-header">
<h5 className="customModal__text"> Appointment summary</h5>
<button className="btn modal__button__red" onClick={() => { this.openDeleteAppointment() }}>Delete</button>
{this.state.openDeleteAppointment &&
<DeleteAppointmentModal appointment={this.state.id} onHide={() => this.setState({ openDeleteClient: false, id: null })} show />}
</div>
<div className="modal-container">
<div className="summary">
<button className="btn modal__button__cancel" onClick={this.handleRemove}>Cancel</button>
</div>
</div>
}
</React.Fragment>
);
}
export default CalenderModal;
child modal:
class DeleteAppointmentModal extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.appointment,
};
}
render() {
const {id} = this.state
const DELETE_MUTATION = gql`
mutation DeleteMutation($id:ID! ) {
deleteAppointment(id:$id) {
id
}
}
`
console.log("delete id",this.state.id)
return (
<React.Fragment>
{
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-update-client"
centered
>
<Modal.Header closeButton >
<Modal.Title id="contained-modal-title-vcenter" className="tittle">Delete appointment </Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="delete-content">
Are you sure you want to delete this appointment?
</div>
</Modal.Body>
<Modal.Footer>
<button onClick={() => this.props.onHide() } className="btn no">No</button>
<Mutation mutation={DELETE_MUTATION}
variables={{id}}>
{/* onCompleted={() => this.props.history.push('/')} */}
{deleteMutation =>
<button onClick={() => { deleteMutation(); this.props.onHide() }} className="btn yes">Yes</button>
}
</Mutation>
</Modal.Footer>
</Modal>
}
</React.Fragment>
);
}
}
export default DeleteAppointmentModal;
On "return" of parent component change the following row:
from:
<DeleteAppointmentModal appointment={this.state.id} onHide={() => this.setState({ openDeleteClient: false, id: null })} show />}
to:
<DeleteAppointmentModal appointment={this.state.id} onHide={() => { this.setState({ openDeleteAppointment: false, id: null }); handleRemove(); }} show />}
Hope it solved the problem.
From observation:
the show prop is always true. set show={this.state. openDeleteAppointment}
the onHide is not setting the right state. it should set openDeleteAppointment instead of openDeleteClient
Related
using login function as component throws an error like this:
'LoginModal' is not defined react/jsx-no-undef
I dont know why this happening.
Thanks in advance.
export default class Login extends Component {
state={
loading: false,
modalShow: false,
clicked: false,
password:''
}
LoginModal = () => {
return <Modal {...this.props} size="lg" aria-labelledby="contained-modal-title-vcenter">
<Modal.Header closeButton>An otp has been sent to your Email!</Modal.Header>
<p>something...</p>
<Button variant="outline-primary" onClick={this.setState({modalShow:false})} >Change Password</Button>
</Modal>
}
handleForgetPassword = () => {
this.setState({modalShow: true})
}
handleSubmit = (event) => {
.....
}
render() {
return (
<div>
<div id="login-wrapper">
<p onClick={this.handleForgetPassword} className="forgot-password">forgot password?</p>
</div>
</div>
<LoginModal
show={this.state.modalShow}
onHide={() => this.setState({modalShow:false})}/>
</div>
)
}
}
You should initialize the state in the constructor
https://reactjs.org/docs/react-component.html#constructor
constructor(props) {
super(props);
this.state={
loading: false,
modalShow: false,
clicked: false,
password:''
}
Also, fix the render method as it's having a missing , or an extra closing tag
render() {
return (
<div>
<div id="login-wrapper">
<p onClick={this.handleForgetPassword} className="forgot-password">forgot password?</p>
</div>
<LoginModal
show={this.state.modalShow}
onHide={() => this.setState({modalShow:false})}/>
</div>
)
}
You're not referencing LoginModal correctly. You should do something like this instead:
Create a standalone component and then pass props to it
const LoginModal = ({onClick, ...props}) => {
return <Modal {...props} size="lg" aria-labelledby="contained-modal-title-vcenter">
<Modal.Header closeButton>An otp has been sent to your Email!</Modal.Header>
<p>something...</p>
<Button variant="outline-primary" onClick={onClick} >Change Password</Button>
</Modal>
}
Try this way
return (
<div>
......
{this.LoginModal()}
</div>
)
CardComponent:
export class Card extends Component<Prop, State> {
state = {
isCancelModalOpen: false,
};
marketService = new MarketService();
deleteMarket = () => {
this.marketService
.deleteMar()
.then((response) => {
})
.catch((error) => {
console.log(error);
});
};
handleModalToggle = () => {
this.setState(({ isCancelModalOpen }) => ({
isCancelModalOpen: !isCancelModalOpen,
}));
};
render() {
const {isCancelModalOpen, isDropdownOpen } = this.state;
const dropdownItems = [
<DropdownItem
key="action"
component="button"
onClick={this.handleModalToggle}
>
Delete
</DropdownItem>
];
return (
{this.CancelModal.map((listing) => (
<DeleteModal handleModal={this.handleModalToggle}
deleteProd = {this.deleteProduct}
description = {listing.description} ></DeleteModal>
))}
);
}
}
DeleteModal Component:
interface Prop {
description: string;
handleModal: Function;
deleteProd: Function;
}
class DeleteModal extends Component<Prop, State> {
state = {
deleteConfirmModel: false
};
render() {
const { deleteConfirmModel } = this.state;
const {description} = this.props;
return (
<Modal
isSmall
title="Confirmation"
isOpen={deleteConfirmModel}
onClose={() => this.props.handleModal}
showClose = {false}
actions={[
<Button
key="confirm"
variant="primary"
onClick={() => this.props.deleteProd}
>
Delete
</Button>,
<Button key="cancel" variant="link" onClick={() => this.props.handleModal}>
Cancel
</Button>
]}
>
{description}
</Modal>
);
}
}
export default DeleteModal;
I want DeleteModal component to get open on click of Dropdown Delete button available in Card component. There is no error in the code, still I am not able to trigger modal on click of Dropdown from Card component. Can anyone help me with what's wrong with the code?
So I found the solution to call the modal from different component:
In the card component: to call deleteModal component:
<DeleteModal
displayModal={deleteModalOpen}
handleModal={this.handleModalToggle}
description="Are you sure you want to delete"
/>
DisplayModal, handleModal,decription will be props in DeleteModal component:
DeleteModal:
interface Prop {
displayModal: boolean;
handleModal: Function;
description: string;
}
<Modal
isSmall
title="Confirmation"
isOpen={this.props.displayModal}
onClose={() => this.props.handleModal()}
showClose={false}
actions={[
<Button
key="cancel"
variant="link"
onClick={() => this.props.handleModal()}
>
Cancel
</Button>,
]}
>
{this.props.description}
</Modal>
Here I am facing some issue to open the modal dialog based on its item key.
I am creating a bog page where i want to display my blogs and open/ close the modal according to its key. and I want to use pagination as well to display 10 blogs per page.
But somehow its taking same data for every modal open .
any suggestion how can i handle this modal open and close?
Thanks..
//blog.js
class Blogs extends Component{
constructor(props) {
super(props);
this.state = {
modal: false
};
this.handleOpenDialog = this.handleOpenDialog.bind(this);
this.handleCloseDialog = this.handleCloseDialog.bind(this);
}
static propTypes = {
getContact: PropTypes.func.isRequired,
deleteContact: PropTypes.func.isRequired,
resume: PropTypes.object.isRequired,
isAuthenticated: PropTypes.bool,
auth: PropTypes.object.isRequired,
loading: PropTypes.object.isRequired
}
toggle = () => {
this.setState({
modal: !this.state.modal
});
}
handleOpenDialog(id) {
this.setState({
openDialog: true,
justClicked: id
});
}
handleCloseDialog() {
this.setState({
openDialog: false
});
}
componentDidMount() {
this.props.getBlog();
}
onDeleteBlogClick = (id) => {
this.props.deleteBlog(id);
};
cardDialog(blogs, user){
return(
<Grid>
{blogs.map(({ _id, blog_short_desc, blog_name, blog_desc, blog_image_link, blog_by }) => (
<Cell col={12}>
<Dialog open={this.state.openDialog} className="open-dialog">
<FABButton onClick={this.handleCloseDialog} className="close-button" mini ripple>
<Icon name="close" />
</FABButton>
<DialogTitle>{blog_short_desc}</DialogTitle>
<img className="blogs-contents-image" src={blog_image_link} alt="blogs"/>
<h4>{blog_name}</h4>
<DialogContent className="dialog-contents">{blog_desc} </DialogContent>
</Dialog>
</Cell>
))}
</Grid>
)
}
render(){
const { blogs, loading} = this.props.resume;
const { isAuthenticated, user } = this.props.auth;
return(
<Container>
{loading ? (
<div><Loading/></div>
) : (
<div>
{ isAuthenticated ?
<Button
color="dark"
style={{marginBottom: '2rem'}}
onClick={this.toggle}
>
Add Blog
</Button> : <h4 className="mb-3 ml-4"> Please login to manage blog</h4> }
{this.cardDialog(blogs, user)}
<Grid id="todo">
{blogs.map(({ _id, blog_name, blog_by, date }) => (
<Cell key={_id} data-id={_id}>
{ this.props.isAuthenticated && (user.is_admin === true) ?
<Button className="remove-btn"
color="danger"
size="sm"
onClick= {this.onDeleteBlogClick.bind(this, _id)}>
×
</Button> : null }
<Card shadow={5} className="cards-grid">
<CardTitle className="card-title-image"></CardTitle>
<CardText>
<b>{blog_name}</b>
</CardText>
<CardActions border>
<Button onClick={this.handleOpenDialog.bind(this, _id)}>Read</Button>
<p style={{ fontStyle:'italic', fontWeight:'bold'}}>By-{blog_by} <span style={{float:'right',}}>{Moment(date).format('Do MMMM YYYY')}</span></p>
</CardActions>
</Card>
</Cell>
))}
</Grid>
</div>
)}
</Container>
)
}
}
const mapStateToProps = (state) => ({
resume: state.resume,
isAuthenticated : state.auth.isAuthenticated,
auth: state.auth,
loading: state.apiCallsInProgress > 0
});
export default connect(mapStateToProps, {getBlog, deleteBlog }) (Blogs);
//JSON
[
{
"_id": "5e9b895ae3929322e426eba5",
"blog_short_desc": "Hey tammy",
"blog_name": "ssfs",
"blog_desc": "jhjhkj",
"blog_image_link": "https://www.shutterstock.com/image-photo/studio-shot-lovely-woman-wears-halloween-1523214461",
"date": "2020-04-18T23:12:26.415Z",
"__v": 0
},
{
"_id": "5e9b7f3a3e382b0a609409fa",
"blog_short_desc": "heey siri",
"blog_name": "sirirrrheyyy siri",
"blog_desc": "hey siri",
"blog_image_link": "https://www.shutterstock.com/image-photo/studio-shot-lovely-woman-wears-halloween-1523214461",
"date": "2020-04-18T22:29:14.850Z",
"__v": 0
}
]
//current visual
I think if you change
<Dialog open={this.state.openDialog} className="open-dialog">
to
<Dialog open={this.state.openDialog && this.state.justClicked === _id} className="open-dialog">
It should work.
Currently it's displaying every modal when openDialog is true. You just can't see it as they are layered on the screen.
I'm currently building a new React application, and am having a bit of trouble understanding the basics; I'm a complete beginner.
Bascially, what I need to do is have a button that, when clicked, opens up a container. Here is my thought process:
Create a main component, named "App". Within App, return a button.
The button, when clicked, should call another component that opens the container.
Some of what I have started on the code so far:
class handleClick extends React.Component{
constructor(props){
super(props);
this.state = {
isClicked: false
};
render(){
return(
);
}
}
}
function App(props){
//HAVE A COMPONENT TO TRIGGER THE CONTAINER
return(
<div>
<button>
Open photo entry dialog
</button>
</div>
);
I have no idea what to do. Any logic tips?
Thanks in advance!
To create model popup from parent to child component. Sample Demo App using react-bootstrap
npm install react-bootstrap bootstrap
Sample Code
App.js
import Model from "./Model";
class App extends React.Component {
state = { status: false };
handleClick = e => {
this.setState(prev => ({ status: !prev.status }));
};
render() {
const { status } = this.state;
return (
<>
<button onClick={this.handleClick}>Click to opent model</button>
{status && <Model status={status} handleClick={this.handleClick} />}
</>
);
}
}
Model.js
import "bootstrap/dist/css/bootstrap.min.css";
import { Button, Modal } from "react-bootstrap";
export default function Model({ handleClick, status }) {
return (
<>
<Modal show={status} onHide={handleClick}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClick}>
Close
</Button>
<Button variant="primary" onClick={handleClick}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
);
}
https://o1z0p.csb.app/
I am using react for the project. Here is my code for parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
deleteConfirm: false
};
}
onDelete = pass => {
this.setState({ deleteConfirm: false });
if (pass === true) {
const { menu, onDelete, restaurantId } = this.props;
//onDelete(menu.id, restaurantId);
}
};
openDiag() {
this.setState({ deleteConfirm: true });
}
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
<Delete className={classes.rightIcon} />
</Button>
</div>
);
}
}
The parent component has state called deleteConfirm, If I press Delete button, deleteConfirm is set to true and the child component is rendered. The function OnDelete passes to child component,
the child component is a Material Ui Dialog which is used to confirm the delete action. Here is the code:
class Child extends Component {
state = {
open: true
};
handleClose = (e, confirm) => {
const { onDelete } = this.props;
if (confirm === true) {
onDelete(true);
} else {
onDelete(false);
}
this.setState({ open: false });
};
render() {
return (
<div>
<Dialog
open={this.state.open}
onClose={e => this.handleClose(e, false)}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogActions>
<Button onClick={e => this.handleClose(e, false)}
color="primary">
Cancel
</Button>
<Button
onClick={e => this.handleClose(e, true)}
color="primary"
autoFocus
>
Confirm
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
If I press the cancel button, the delete action is cancelled and the parent function onDelete is called. the deleteConfirm in parent component should be set to false. However, the state is always true and I can't change it. I am greatly appreciated for anybody who would like to help me. Thanks!
Classic Event Propagation mistake. You embedded a click handler inside another click handler which reversed the logic of the inner one.
Use event.stopPropagation in your inner handler or rearrange your elements such that they are not nested within one another:
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
<Delete className={classes.rightIcon} />
</Button>
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
</div>
);