Material UI Dialog not closing when clicking the overlay - reactjs

I have created a reusable dialog/modal using React and Material UI.
When I try to click the overlay, something weird happens.
Instead of closing the modal and setting 'open' to false, the scripts directly sets open to true again for some reason.
The handleClickOpen is triggered when clicking the overlay and I can't find the problem causing this.
Dialog component:
const { onClose, open } = props;
const handleClose = () => {
onClose();
};
const preventDef = (e) => {
e.stopPropagation();
};
return (
<Dialog open={open} onClose={onClose} className={className} onClick={handleClose}>
<Container>
<Grid container className={`${className}__container`}>
<Grid item xs={12} sm={props.colsAmount} onClick={preventDef} className={`${className}__column`}>
{props.component}
{props.closeButton && (
<div className={`${className}__close`} onClick={handleClose}>
<Close />
</div>
)}
</Grid>
</Grid>
</Container>
</Dialog>
);
The component I load and trigger the dialog in:
const [open, setOpen] = React.useState(false);
const handleClose = (e) => {
setOpen(false);
};
const handleClickOpen = () => {
setOpen(true);
};
return (
<div className={[classes.ShopCard]} onClick={handleClickOpen}>
<div className={`${classes.ShopCard}__image`}>
<div className={`${classes.ShopCard}__image__wrapper`}>
<img src={image} alt={alt} />
<div className={`${classes.ShopCard}__image__price`}>Vanaf €{price}</div>
</div>
</div>
<div className={`${classes.ShopCard}__content`}>
<Title title={title} size="h6" />
<Body>{description}</Body>
</div>
<div className={`${classes.ShopCard}__button`}>
<Button label="In winkelwagen" startIcon={<AddShoppingCart />} />
</div>
<CustomDialog
open={open}
onClose={handleClose}
colsAmount={6}
component={<ShopPopUp />}
closeButton="true"
/>
</div>
If someone could explain why the open state gets set to true, that would be great.

It's because of:
<div className={[classes.ShopCard]} onClick={handleClickOpen}>
It exists higher up in the DOM than the overlay thus it always fires.

Related

Passing click function from One component to other component

In React material ui i am having two components where i am calling save function on button click, is it right way or not can anyone suggest the better way:
const callback = {};
return (
<>
{!state?.creditCard?.isSaved ? (
<Paper elevation={4} className={classes.paymentContainer}>
<Box className={classes.subPaymentContainer}>
<Typography className={classes.title}>Card Payment</Typography>
<CardPaymentForm
callback={callback}
validationPassed={() => actionsCollection.booking.saveCard(true, state.creditCard.lastFourDigits)}
formType="profileForm"
/>
<div>
<Button
type="submit"
onClick={(e) => callback.saveCard(e)}
value="SAVE CREDIT CARD"
className={classes.button}
/>
<div style={{ display: "flex", marginTop: 20 }}>
<img className={classes.lockIcon} src={lockIconInfo} alt="" />
<Typography className={classes.paymentInfo}>
<Link href="/terms" target={"_blank"}>
Terms of Payment
</Link>
.
</Typography>
</div>
</div>
</Box>
</Paper>
) : (
<div style={{ height: 373 }}>
<CardStored removeCard={removeCard} />
</div>
)}
</>
);
in CardPayementForm below calling the save function below is the code:
const CardPaymentForm = ({ classes, callback, validationPassed, formType, lastFourDigits }) {
useEffect(() => {
callback.saveCard = (e) => {
e.preventDefault();
=
if (validateForm()) {
=
validationPassed();
}
};
});
}
here without callback how to call save function directly in cardpaymentform, Any help please
I'm not sure this will apply to your problem but if you had a component_a
like
const ComponentA = ({handleClick}) => {
return(
<>
<button onClick(e => handleEvent(e))>
Click here
</button>
</>
}
and a component_b
const ComponentB = () => {
const handleClick = (e) => {
// do something with the event from component a
}
return(
<>
<ComponentA handleClick={handleClick}/>
</>
)
}

Show popup from another component using functions in react (.jsx)?

Hello there I'm new with Reactjs and just have a quick question.
I have a modal component that I want to show up when a function is called from another component.
The modal component
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
const toggle = () => {
setOpen(!open);
};
return (
<div>
<Button onClick={toggle}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={toggle}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;
This is the function that I need to show up the popup when it's called
const finishPayment = () => {
console.log('just checking if this function is called properly');
return (
<Popup
toggle={this.toggle}
title='Success'
body='Welcome, everything was fine.'
/>
);
};
Can someone help me how to figure out this?
I think I need to pass something as props, but not sure what.
Thanks!
Your buttons are using "onClick" events, therefore, you must pass a function as argument, but you are passing a constant. Convert your "toggle()" arrow function to function or implement another arrow function inside the "onClick" events, so as in the examples below:
With arrow function:
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
const toggle = () => {
setOpen(!open);
};
return (
<div>
<Button onClick={() => toggle()}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={() => toggle()}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;
With vanilla/traditional functions:
const Popup = (props) => {
const [open, setOpen] = React.useState(false);
function toggle() {
setOpen(!open);
};
return (
<div>
<Button onClick={toggle}>Click Me</Button>
<Modal className='successModal text-center' open={open} centered={true} size='lg'>
<ModalHeader>{props.title}</ModalHeader>
<ModalBody>
<div>
<img src={successIcon} alt='Success Icon' />
<div className='container my-4'>
<h4>{props.body}</h4>
</div>
</div>
<Button onClick={toggle}>Close</Button>
</ModalBody>
</Modal>
</div>
);
};
export default Popup;

How do I see modal by clicking image in react??(instagram clone coding)

import { Modal } from "antd";
function Profile(){
const [visible, setVisible] = useState(false);
return(
<>
{myposts.map((mypost, index) => {
return (
<>
<img
key={index}
className="item"
onClick={() => {
setVisible(true);
}}
src={`http://localhost:5000/uploads/${mypost.photo}`}
/>
{visible && (
<Modal
title="Basic Modal"
visible={visible}
onOk={setVisible(false)}
onCancel={setVisible(false)}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
)}
</>
);
})}
</>
)}
This is the summery of my code. I think when I click image, it should makes modal. But, It doesn't work anything. I don't know which part is wrong. (Modal part has some strange content now. I'll change this part after seeing modal.)
Try to place the modal outside the mypost map, maybe this is why the modal won't show up. You may need another state for the src path of the selected image.
import { Modal } from "antd";
function Profile() {
const [visible, setVisible] = useState(false);
const [selectedImgSrc, setSelectedImgSrc] = useState("");
const imgClick = (imgSrc) => {
setSelectedImgSrc(imgSrc);
setVisible(true);
};
return (
<>
{myposts.map((mypost, index) => {
return (
<>
<img
key={index}
className="item"
onClick={() => {
imgClick(`http://localhost:5000/uploads/${mypost.photo}`);
}}
src={`http://localhost:5000/uploads/${mypost.photo}`}
/>
</>
);
})}
<Modal
title="Basic Modal"
visible={visible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
>
<img src={selectedImgSrc} />
</Modal>
</>
);
}
it seems like the problem is here in your code.
<Modal
title="Basic Modal"
visible={visible}
onOk={setVisible(false)}
onCancel={setVisible(false)}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
onOk={setVisible(false)}
onCancel={setVisible(false)}
The problem is these lines.
When you click an image it opens your modal, but when the modal being to render, these lines are setting visible state as false. So the modal will be deleted from DOM soon.
That's why you can not see your modal on clicking an image.
Please try with this.
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
You should pass a function for onOk and onCancel props.

How can i call a function inside another Component with React?

I have a component (Navigation.js) which imports another component (Dialog.js). I want that if I react to a click event, call a function in the dialog component (handleClickOpen ()). But I don't know how to do that. So what i have to do ?
Navigation.js
export default function SimpleBottomNavigation() {
return (
<BottomNavigation
value={value}
onChange={(event, newValue) => {
setValue(newValue);
}}
showLabels
className={classes.root}
>
<BottomNavigationAction
label="Home"
onClick={'HERE I WANT TO CALL THE FUNCTION IN THE DIALOG COMPONENT'}
icon={<RestoreIcon />}
/>
<BottomNavigationAction label="Neuer Plan" icon={<FavoriteIcon />} />
<BottomNavigationAction label="Azubis" icon={<LocationOnIcon />} />
</BottomNavigation>
);
}
Dialog.js
export default function CustomizedDialogs() {
const [open, setOpen] = React.useState(false);
*/THIS FUNCTION I WANT TO CALL FROM NAVIGATION.JS */
const handleClickOpen = () => {
setOpen(true);
};
[...]
return (
<div>
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open dialog
</Button>
<Dialog
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
Modal title
</DialogTitle>
<DialogContent dividers>
</Dialog>
</div>
);
}
There is a aimple way to use child's functions, with functional components it's forwardRef and useImperativeHandle, along those lines:
Navigation (parent)
function Navigation() {
const dialogRef = useRef();
return(
<button onClick={() => dialogRef.current.handleClickOpen()}>
Click me!
</button>
);
}
Dialog (child)
const Dialog = forwardRef((props, ref) => {
useImperativeHandle(ref, () => {
const handleClickOpen = () => {
//your implementation
};
});
return (...);
});
If I over-simplified, please let me know :)

React - how to invoke popup window in my case?

I'm not a React expert yet thus I have a question for you - how to invoke my popup window from:
import {options, columns,convertToArray} from './consts'
const index = () => {
const {data, loading, error, performFetch} = fetchHook({path: "/xxx/yyy", fetchOnMount: true})
return (
<div className={classes.Container}>
<h1>List of products</h1>
<Divider className={classes.Divider} />
<ProductTable data={convertToArray(data)} options={options} columns={columns}/>
</div>
)
}
export default index;
consts.js
export const actions = (productPropertyId, showModal) => {
const productDetails = (productPropertyId) => {
}
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
return (
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
const removeProduct = (productPropertyId, showModal) => {
actions(productPropertyId, showModal);
>{"Remove"}
</Button>
</div>
)
};
export const convertToArray = (productList) => {
let products = []
if (productList != null) {
productList.map(product => {
column1, column2, column3, actions(product.id)]
products.push(prod)
})
}
return products;
};
My popup is --> <FormDialog/> based on react Materials.
Is it possible to invoke popup in this place?
I have a react material table with some columns. The last column contains 2 buttons, one of them is "Remove" (row). Here I want to invoke my popup. Maybe I should rebuild my structure?
UPDATE
Below is my popup - I wonder how to run this popup from the place above:
const formDialog = (popupOpen) => {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open alert dialog*/}
{/*</Button>*/}
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send updates
occasionally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default formDialog;
UPDATE 2
I updated my code taking into cosideration the tips from your response, see above. Can I add a parameter showModal in my export const actions = (productPropertyId, showModal) and then invoke this component with different showModal value? UNfortunately my popup doesn't appear when I click on Remove button :(
You can invoke it conditionally and controle it using some state variable. Like this:
const [removeModal, removeToggle] = useState(false);
return (<>
<div className={classes.actionsContainer}>
<Button
onClick={() => productDetails(productPropertyId)}
> {"More"}
</Button>
<Button
onClick={() => removeToggle(true)}
>{"Remove"}
</Button>
</div>
{removeModal ? <YourComponent /> : ''}
</>
)
I'm using a react fragment there <></> just to position the modal div outside the main div, but you can also invoke it inside your main div as well (I usually do this and set the position: fixed so the modal/popup coud appear in top of everything).

Resources