I have two modals in React:
<Modal isOpen={this.state.activeModal === 'addedToCart'} id="#addedToCart" show={this.state.show} onHide={()=>{this.handleModal()}}>
<Modal.Header closeButton> my heading</Modal.Header>
<Modal.Body> Added Successfully to cart!</Modal.Body>
<Modal.Footer>
<Button onClick={()=>{this.handleModal()}}>Continue Shopping</Button>
<Button onClick={()=>{this.handleGo()}}>See Your Cart</Button>
</Modal.Footer>
</Modal>
<Modal isOpen={this.state.activeModal === 'selectQuantity'} id="#selectQuantity" show={this.state.showError} onHide={()=>{this.handleModalError()}}>
<Modal.Header closeButton> My heading</Modal.Header>
<Modal.Body> Please choose quantity first!</Modal.Body>
</Modal>
What I am trying to call different modal from this function if quantity is empty then call modal selectQuantity if quantity is chosen then call second modal addedToCart.
handleSubmit(e,id)
{
e.preventDefault();
if(!this.state.qty) {
$('#selectQuantity').openModal();
}
else {
axios.post('add',{qty:this.state.qty,
id:id})
$('#addedToCart').openModal();
}
}
Anyone has idea how to do this?
I think:
handleSubmit(e, id) {
...
if(!this.state.qty) {
this.setState({ activeModal: "selectQuantity" })
} else {
...
this.setState({ activeModal: "addedToCart" })
}
}
Related
I am trying to implement a Success Confirmation popup modal after a successful axios.delete call.
The delete is working and even console.log() works inside of my conditional rendering but I've noticed that my initial state is false, on successful delete (inside of my try block) the state changes to true but then once the component re-renders it chnages back to false, causing my popup modal to not render.
I'm not sure if the try block is the issue or the way I'm trying to render the popup modal.
Initial State
const [showSuccess, setShowSuccess] = useState(false);
axios request
const deleteService = async (id: number) => {
try {
const JWT = await getCookie("auth");
const { data } = await axios(
`/api/serviceType/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: JWT,
},
});
setData(data);
// Success Alert Popup
setShowSuccess(true);
} catch (e) {
// Error Alert Popup
setShowAlert(true);
}
};
The alert state change inside of the catch block works as needed!
Conditional Render
// Table state update on submit
useEffect(() => {
fetchData(data);
}, [data]);
// Success Alert
if (showSuccess === true) {
return (
<>
<Modal show={show} onHide={() => {setShowSuccess(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#00E676"}}></Modal.Header>
<AlertDialog
title={"Success!"}
message={"Service Type was successfully deleted."}
/>
</Modal>
</>
)
}
if (showAlert === true) {
return (
<>
<Modal show={show} onHide={() => {setShowAlert(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#FF1744"}}></Modal.Header>
<AlertDialog
title={"Error Deleting Data"}
message={"There was an error deleting the Service."}
/>
</Modal>
</>
)
}
return (
<>
<Trash onClick={handleShow}/>
<Modal show={show} backdrop="static" onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Delete Service</Modal.Title>
</Modal.Header>
<Modal.Body>
Are you sure you want to delete this Service? This process cannot be undone.
</Modal.Body>
<Modal.Footer>
<Button variant="outline-dark" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" variant="danger" onClick={() => deleteService(id)}>
Delete
</Button>
</Modal.Footer>
</Modal>
</>
);
The error modal and confirm modal work, but the success modal is not.
Entire Component
import React, { useState, useEffect } from 'react';
import { getCookie } from "../../../utils/cookies";
import axios from "axios";
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import { Trash } from 'react-bootstrap-icons';
import AlertDialog from '../../../alerts/AlertDialog';
export default function DeleteService({ fetchData, id }) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState([]);
// Success Dialog
const [showSuccess, setShowSuccess] = useState(false);
console.log(showSuccess)
// Error Dialog
const [showAlert, setShowAlert] = useState(false);
// DELETE
const deleteService = async (id: number) => {
try {
const JWT = await getCookie("auth");
const { data } = await axios(
`/api/serviceType/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
Authorization: JWT,
},
});
setData(data);
setIsLoading(false);
// Hides Modal on submission
setShow(false);
// Success Alert Popup
setShowSuccess(true);
} catch (e) {
setIsLoading(false);
// Error Alert Popup
setShowAlert(true);
}
};
// Table state update on submit
useEffect(() => {
fetchData(data);
}, [data]);
// Success Alert
if (showSuccess === true) {
return (
<>
<Modal show={show} onHide={() => {setShowSuccess(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#00E676"}}></Modal.Header>
<AlertDialog
title={"Success!"}
message={"Service was successfully deleted."}
/>
</Modal>
</>
)
}
if (showAlert === true) {
return (
<>
<Modal show={show} onHide={() => {setShowAlert(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#FF1744"}}></Modal.Header>
<AlertDialog
title={"Error Deleting Data"}
message={"There was an error deleting the Service."}
/>
</Modal>
</>
)
}
return (
<>
<Trash onClick={handleShow}/>
<Modal show={show} backdrop="static" onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Delete Service</Modal.Title>
</Modal.Header>
<Modal.Body>
Are you sure you want to delete this Service? This process cannot be undone.
</Modal.Body>
<Modal.Footer>
<Button variant="outline-dark" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" variant="danger" onClick={() => deleteService(id)}>
Delete
</Button>
</Modal.Footer>
</Modal>
</>
);
}
You set show to false immediately before setting showSuccess to true. Remember all of your modals rely on show.
You already have separate variables to show the other modals, so why not use them?
Change show to showDelete (for clarity) and change your return to:
return (
<>
<Trash onClick={handleShow}/>
<Modal show={showDelete} backdrop="static" onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Delete Service</Modal.Title>
</Modal.Header>
<Modal.Body>
Are you sure you want to delete this Service? This process cannot be undone.
</Modal.Body>
<Modal.Footer>
<Button variant="outline-dark" onClick={handleClose}>
Cancel
</Button>
<Button type="submit" variant="danger" onClick={() => deleteService(id)}>
Delete
</Button>
</Modal.Footer>
</Modal>
<Modal show={showSuccess} onHide={() => {setShowSuccess(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#00E676"}}></Modal.Header>
<AlertDialog
title={"Success!"}
message={"Service was successfully deleted."}
/>
</Modal>
<Modal show={showAlert} onHide={() => {setShowAlert(false); handleClose(); }} backdrop="static">
<Modal.Header closeButton style={{ backgroundColor: "#FF1744"}}></Modal.Header>
<AlertDialog
title={"Error Deleting Data"}
message={"There was an error deleting the Service."}
/>
</Modal>
</>
);
Delete both if statements.
If that's too cluttered for you, you can put the modals into variables for organization.
const deleteModal =
<Modal show={showDelete} backdrop="static" onHide={handleClose}>
...
</Modal>
const successModal =
<Modal show={showSuccess} onHide={() => {setShowSuccess(false); handleClose(); }} backdrop="static">
...
</Modal>
const alertModal =
<Modal show={showAlert} onHide={() => {setShowAlert(false); handleClose(); }} backdrop="static">
...
</Modal>
return (
<>
<Trash onClick={handleShow}/>
{deleteModal}
{successModal}
{alertModal}
</>
);
In your deleteService function you call setShow(false); which causes the modal to be hidden, because you pass "show" as prop to your Modal even if showSuccess is true.
The confusion is created by variable name "show" that doesn't tell anything specific about its value, so at the end is used incorrectly
class dashboard extends Component {
constructor(props) {
super(props);
this.state = {
show: false,
msg: false
}
}
handelModal1() {
this.setState({ show: !this.state.show })
}
handelModal2() {
this.setState({ msg: !this.state.msg })
}
render() {
return (
<div>
<>
<Button variant="primary" onClick={() => { this.handelModal1() }}
one
</Button>
<Modal show={this.state.show} >
<Modal.Header>
<Modal.Title>Modal one</Modal.Title>
</Modal.Header>
<Modal.Body> hello </Modal.Body>
<Modal.Footer>
<Button onClick={() => { this.handelModal1() }}>
OK
</Button>
</Modal.Footer>
</Modal>
</>
</div>
<div>
<>
<Button variant="primary" onClick={() => { this.handelModal2() }}
two
</Button>
<Modal msg={this.state.msg} >
<Modal.Header>
<Modal.Title>Modal two</Modal.Title>
</Modal.Header>
<Modal.Body> hello </Modal.Body>
<Modal.Footer>
<Button onClick={() => { this.handelModal2() }}>
OK
</Button>
</Modal.Footer>
</Modal>
</>
</div>
)
}
I am trying to use two modals on the same page.
The first modal is working and I'm getting the pop-up, but the second modal is now working.
There is no pop up when I click on the second modal. Is there any simple way to use two modals on the same page???
You have to set <Modal show={this.state.msg} > Instead<Modal msg={this.state.msg} >
or
Maybe this link will help you React Rendering Multiple Modals in Same Component
On button click i was trying to open a model (and modal opening too) and in a same modal it contain a button and on button click i was trying to open another model (and second modal opening too), but when second modal is opening i want first model to be closed. can it be possible?
Here is my sandbox demo https://codesandbox.io/embed/dreamy-herschel-cyetn?fontsize=14&hidenavigation=1&theme=dark
const Practice = () => {
const [modalShow, setModalShow] = useState(false);
const handleSubmit = event => {
setModalShow(true);
};
return (
<div>
<Button onSubmit={handleSubmit} type="submit">
Submit
</Button>
<Modals show={modalShow} onhide={() => setModalShow(false)} />
</div>
);
};
here is my modal part
const Modals = ({ show, onhide }) => {
const [modalShowsec, setModalShowsec] = useState(false);
const Validation = () => {
setModalShowsec(true);
};
return (
<div>
<Modal show={show} onHide={onhide} size="sm" aria-labelledby="contained-modal-title-vcenter" centered>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">HELLO</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>Hi</p>
</Modal.Body>
</Modal>
<button onClick={Validation}> Validate </button>
<Modal show={modalShowsec} onHide={() => setModalShowsec(false)}>
<Modal.Header closeButton />
<Modal.Body>
<p>Hi cool</p>
</Modal.Body>
</Modal>
</div>
);
};
Call onhide inside Validation function. This will hide the first modal.
const Validation = () => {
setModalShowsec(true)
onhide()
}
What I want to do is add bootstrap-react modal and fire it from a stateless function. All the examples I can find requires changing the "show" in the state of component, but since Im not using af component I don't really have an idea how to do it.
https://react-bootstrap.github.io/components/modal/
https://react-bootstrap.github.io/components/modal/
You need state somewhere to show the modal. You can use hooks to do it in functional (not really "stateless") component using useState.
function App() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="primary" onClick={() => setOpen(true)}>
Launch demo modal
</Button>
<Modal show={open} onHide={() => setOpen(false)}>
<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={() => setOpen(false)}>
Close
</Button>
<Button variant="primary" onClick={() => setOpen(false)}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
);
}
codesandbox: https://codesandbox.io/embed/z2ky5l128l
If you don't want to do it then you need to pass prop from component that is higher in tree, like:
class App extends React.Component {
state = {
open: true,
}
render() {
return <ModalComponent open={open} hide={() => this.setState({open: false})} />
}
}
function ModalComponent = ({open, hide}) => (
<Modal show={open} onHide={hide}>
<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={hide}>
Close
</Button>
</Modal.Footer>
</Modal>
)
just pass visible state from parrent like below
class parent extends Component {
state = {
modalVisibility : false
}
handleModalVisibility = (action)=> this.setState({modalVisibility : action})
return (
<div>
<button onClick={this.handleModalVisibility.bind(this,true)} >Show Modal</button>
//use this props to show or cancel the modal
<ModalComponent visibility ={this.state.modalVisibility} handleModal={this.handleModalVisibility} />
</div>
)
}
I guess you can do that with useState
https://reactjs.org/docs/hooks-state.html
New to programming so I'm sorry if I'm not wording this correctly. I'm using a .map to render and list every single item on an array. For each item, I want the modal to open/close only the specific modal corresponding to each item in the array. However, when I click on the button to open the modal, every single one opens and closes. I believe this is because the modals are all set to an on/off button together. How can I set it it (with the .map value.id or something) so that only the specific modal opens and closes?
class DrinkMenu extends Component {
constructor(props, context) {
super(props, context);
this.state = {
show: false
};
this.handleHide = this.handleHide.bind(this);
}
handleHide() {
this.setState({ show: false });
}
async componentDidMount() {
let res = await axios.get('/getAllDrinks')
this.props.updateDrinkMenu(res.data)
}
async addToCart(drinkObj) {
let res = await axios.post('/addToCart', drinkObj)
console.log(`Added ${drinkObj.name} to order.`)
}
render() {
let drinkList = this.props.drinkMenu.map((drink) => {
return (
<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
})
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
}
From the code you have shared, I see that you are handling all the Model with the same state value, i.e. show. This is causing all the state for all the Models to be true hence all of them as shown.
To solve this, you can extract your whole component in a new React class which has just the functionality to show Modal as per the independent state. So your new React component will look something like this:
class DrinkComponent extends React.Component {
constructor(props) {
super(props);
this.handleHide = this.handleHide.bind(this);
this.state = {
show: false,
}
}
handleHide() {
this.setState({ show: false });
}
render() {
const { drink } = this.props;
return (<div key={drink.id}>
<h5>{drink.name}</h5>
<h6>${drink.price}</h6>
<span
onClick={() => this.setState({ show: true })}
>
<strong>More Info</strong>
<br />
<button onClick={() => this.props.addToCart(drink)}>Add to Order</button>
</span>
<Modal
show={this.state.show}
onHide={this.handleHide}
container={this}
aria-labelledby="contained-modal-title"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title">
{drink.name}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{drink.sub_category} | ABV {drink.abv}% | {drink.origin}</p>
<Col xs={6} md={4}>
<Image className="drink-logo" src={drink.logo} thumbnail />
</Col>
<p className="drink-description"><strong>Description</strong><br />{drink.description}</p>
<p href={drink.website}>Website</p>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleHide}>Close</Button>
</Modal.Footer>
</Modal>
</div>);
}
}
In this case, each DrinkComponent will have its independent state of showing and hiding of the model. Now we have to just modify your existing render function in DrinkMenu, to display DrinkComponent. So your render function will look something like this:
render() {
let drinkList = this.props.drinkMenu.map((drink) => (<DrinkComponent drink={drink} addToCart={this.addToCart}/>));
return (
<div>
<h2>Drink Menu</h2>
<div>
{drinkList}
</div>
</div>
)
}
Also you can remove the show state from DrinkMenu as it wont be needed there.
Hope it helps.