I have this component with a modal for confirmation when deleting an item. How can I pass the function to the child component so that once I click the button Agree, it will trigger this function for delete.
The function to delete an item:
const deleteProduct = async (id) => {
const productDoc = doc(db, "products", id);
await deleteDoc(productDoc);
};
The Dialog:
<Modal
title="Confirmation"
subtitle={sample}
isOpen={isOpen}
handleClose={handleClose}
/>
The Reusable component:
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, handleClose }) => {
const handleConfirm = () => {
alert("You Agreed!");
handleClose();
};
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
Cancel
</Button>
<Button onClick={handleConfirm} color="primary">
Agree
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
Simply pass the function and productId as props to Modaland because the function is asynchronous, you have to make the handleConfirm asynchronous as well.
The Dialog:
<Modal
productId={id}
title="Confirmation"
subtitle={sample}
isOpen={isOpen}
deleteProductCallback={deleteProduct}
handleClose={handleClose}
/>
The Reusable component:
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ productId,title, subtitle, children, isOpen, handleClose ,deleteProductCallback}) => {
const handleConfirm = async() => {
alert("You Agreed!");
await deleteProductCallback(productId);
handleClose();
};
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
Cancel
</Button>
<Button onClick={handleConfirm} color="primary">
Agree
</Button>
</DialogActions>
</Dialog>
);
};
Update the reusable component to accept an onConfirm prop and pass deleteProduct in as that argument. I've changed handleClose to onClose here for consistency.
<Modal
title="Confirmation"
subtitle={sample}
isOpen={isOpen}
onClose={handleClose}
onConfirm={() => deleteProduct(id)}
/>
// ...
const Modal = ({ title, subtitle, children, isOpen, onClose, onConfirm }) => {
const handleConfirm = () => {
alert("You Agreed!");
onConfirm();
onClose();
};
// ...
You can pass the deleteProduct function and the id as props to the Modal Components like bellow:
The Dialog:
<Modal
title="Confirmation"
subtitle={sample}
isOpen={isOpen}
id={id}
handleClose={handleClose}
deleteProduct={deleteProduct}
/>
The Reusable component:
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, id, handleClose, deleteProduct }) => {
const handleConfirm = () => {
alert("You Agreed!");
handleClose();
deleteProduct(id)
};
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
Cancel
</Button>
<Button onClick={handleConfirm} color="primary">
Agree
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
Related
I am using https://react-spectrum.adobe.com/react-spectrum/Dialog.html and whenever I close the dialog and reopen it the value I type does not default back to the initial state. How can I render Dialog on close to reset all states within the dialog?
import {
ActionButton,
Button,
ButtonGroup,
Content,
Dialog,
DialogTrigger,
Divider,
Header,
Heading,
Text,
TextField,
} from "#adobe/react-spectrum";
import { useState } from "react";
export const DialogBox = () => {
const [value, setValue] = useState("");
return (
<DialogTrigger>
<ActionButton>Check connectivity</ActionButton>
{(close) => (
<Dialog>
<Heading>Internet Speed Test</Heading>
<Header>Connection status: Connected</Header>
<Divider />
<Content>
<TextField value={value} onChange={setValue} />
</Content>
<ButtonGroup>
<Button variant="secondary" onPress={close}>
Cancel
</Button>
<Button variant="cta" onPress={close}>
Confirm
</Button>
</ButtonGroup>
</Dialog>
)}
</DialogTrigger>
);
};
Run setValue('') before closing
import {
ActionButton,
Button,
ButtonGroup,
Content,
Dialog,
DialogTrigger,
Divider,
Header,
Heading,
Text,
TextField,
} from "#adobe/react-spectrum";
import { useState } from "react";
export const DialogBox = () => {
const [value, setValue] = useState("");
return (
<DialogTrigger>
<ActionButton>Check connectivity</ActionButton>
{(close) => {
const onClose = () => {
setValue('')
close()
}
return (
<Dialog>
<Heading>Internet Speed Test</Heading>
<Header>Connection status: Connected</Header>
<Divider />
<Content>
<TextField value={value} onChange={setValue} />
</Content>
<ButtonGroup>
<Button variant="secondary" onPress={onClose }>
Cancel
</Button>
<Button variant="cta" onPress={onClose }>
Confirm
</Button>
</ButtonGroup>
</Dialog>
)
}
)}
</DialogTrigger>
);
};
#Konrad Linkowski 's is good,
but I would recommend a better structure.
You can read from their docs about: handling events.
In the event handling you can add the setValue(''):
const cancel = (close) => {
setValue('');
close();
};
This question already has an answer here:
How do I use React Modal with a map function?
(1 answer)
Closed 6 months ago.
How can I show the modal only based on card was clicked?
I have this dynamic card. Once I clicked on a specific card, how can it show the modal with a title of the data's id? As of now, once I click on a card, it will only show the last id.
Main app
const [isOpen, setisOpen] = useState(false);
const handleOpen = (id) => {
console.log(id);
setOpen(true)
};
const handleClose = () => {
setisOpen(false);
};
....
{data.map((i) => (
<Grid
key={data.indexOf(i)}
>
<CardonClick={(e) => handleOpen(i.id)}>
<CardHeader title={i.title} />
</Card>
<Modal isOpen={isOpen} handleClose={handleClose} title={i.id}/> <-- display of the id here
</Grid>
))}
Modal reusable component
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, handleClose }) => {
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
No
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
You can use useState hook and store the relevant data (id in your case) in handleOpen function. Then you can use that data in your modal. example:
const [isOpen, setisOpen] = useState(false);
const [currentModalId,setCurrentModalId]=useState("");
const handleOpen = (id) => {
console.log(id);
setCurrentModalId(id);
setOpen(true)
};
const handleClose = () => {
setisOpen(false);
};
And then use it:
...<Modal> <div> {id} </Modal>
This is my reusable component:
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, handleClose }) => {
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
Close
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
Parent component where I use this here:
const [isOpen, setisOpen] = useState(false);
const [isOpenDialog, setIsOpenDialog] = useState(false);
const handleDialogOpen = () => {
setisOpen(true);
};
const handleDialogClose = () => {
setisOpen(false);
};
const handleOpen = () => {
setIsOpenDialog(true);
};
const handleClose = () => {
setIsOpenDialog(false);
};
<Modal
title="confirmation"
isOpen={isOpen}
children={sample}
handleClose={handleDialogClose}
/>
<Button
color="error"
onClick={
() => handleDialogOpen}
>
Delete
</Button>
It does not show any error in the console and the app does not crash. How can I fix this? Also how can I add a button where the user can say yes since at the moment the modal only have a buttin to close it
There's a lot to unpack here. Here's a solution:
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, handleClose }) => {
const handleConfirm = () => {
alert("You Agreed!");
handleClose();
};
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
Cancel
</Button>
<Button onClick={handleConfirm} color="primary">
Agree
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
Here's the parent:
import Modal from "./modal";
import { useState } from "react";
import { Button } from "#mui/material";
export default function App() {
const [isOpen, setisOpen] = useState(false);
const handleOpen = () => {
setisOpen(true);
};
const handleClose = () => {
setisOpen(false);
};
return (
<div className="App">
<Modal title="confirmation" isOpen={isOpen} handleClose={handleClose} />
<Button color="primary" onClick={handleOpen}>
I Agree
</Button>
</div>
);
}
Here's the sandbox:
I am very new to React so this may seem a little trivial. I have a delete icon in one file which when clicked, I am trying to program a confirmation dialog box. Using the source of their website: https://material-ui.com/components/dialogs/. My file is comprises of a listview:
ListView:
import React from 'react';
import PropTypes from 'prop-types';
import { List, ListItem, ListItemText, ListItemAvatar, Avatar, ListItemSecondaryAction, IconButton } from '#material-ui/core';
import DeleteIcon from '#material-ui/icons/Delete';
import AlertDialog from './AlertDialog'
// Import CSS
import './ListViewer.css'
export function ListViewer({ objects}) {
return (
<div className='list-viewer'>
<List>
<ListItem alignItems="center" divider key={obj.id}>
<ListItemText primary={objects.name} />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete" onClick={handleClickOpen()}>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
);
}
AlertDialog.js:
import React from 'react';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
export default function AlertDialog() {
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="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Are you sure you want to delete this object?"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Deleting this object will permanently remove it
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Delete
</Button>
</DialogActions>
</Dialog>
</div>
);
}
As you can see in AlertDialog, there was initially a button which triggers the dialog to open. Instead from my other file, when the delete icon is clicked, I am trying to trigger the dialog. How can I do this? I have imported AlertDialog and AlertDialog.handleClickOpen but this does not work as handleClickOpen is not a function
You can pass open and onClose via props into AlertDialog.
function AlertDialog(props) {
const { open, onClose } = props
return (
<Dialog
open={open}
onClose={onClose}
>
{/* Dialog content */}
</Dialog>
Then, simply use it in ListView:
function ListView() {
const [dialogIsOpen, setDialogIsOpen] = React.useState(false)
const openDialog = () => setDialogIsOpen(true)
const closeDialog = () => setDialogIsOpen(false)
return (
<div className='list-viewer'>
<List>{/* Now you can set dialogIsOpen here */}</List>
<AlertDialog open={dialogIsOpen} onClose={closeDialog} />
</div>
)
}
There are a few steps how I would do it the first is that I would do instead of a open and close function I would do one as this:
const toggleDialog = useCallback(() => {
setOpen(!open);
}, [open]);
The useCallback function makes it that it only creates a new function when the parameter in the [] changes, means when open changes.
Sadly #Code-Apprentice was faster and metioned the rest to it.
<IconButton edge="end" aria-label="delete" onClick={handleClickOpen()}>
The onClick handler here must be in the same class that renders the <IconButton>. Also, remove the parentheses to set the onClick prop to the function instead of its return value:
onClick={handleClickOpen}
I need to create a new data using a modal box and this is how I implemented it but apparently the new data is not being added in the datatable. Is their a way to do this?
Here is my code:
let id = 0;
function createData(name, provider){
id += 1;
return [id, name, provider];
}
const data = [
createData("Dummy1", "oracle"),
createData("Dummy2", "mssql"),
createData("Dummy3", "oracle"),
];
function ModalBox(props){
const [open, setOpen] = React.useState(false);
const [state, setState] = React.useState({
dname: '',
dsource: '',
data
})
const handleChange = name => e =>{
setState({
...state,
[name]: e.target.value,
})
}
const handleClickOpen = () => {
setOpen(true);
}
const handleClose = () => {
setOpen(false);
}
const addDataSource = () =>{
data.push(createData(state.dname, state.dsource));
setOpen(false);
}
return(
<div>
<Button variant="contained" color="primary" onClick={handleClickOpen}>
Create New
</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="Name"
type="text"
value={state.dname || ''}
onChange={handleChange('dname')}
fullWidth
/>
<Select
native
fullWidth
value={state.dsource || ''}
onChange={handleChange('dsource')}
>
<option value="" />
<option value={'mssql'}>mssql</option>
<option value={'oracle'}>oracle</option>
</Select>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={addDataSource} color="primary">
Add
</Button>
</DialogActions>
</Dialog>
</div>
);
}
function TestSource(){
const columns = ["Id", "Name", "Provider"];
const options = {
filterType: 'checkbox',
};
return(
<div className="f-height fx-column-cont">
<MainToolbar/>
<Container>
<ModalBox/>
<MUIDataTable
title={"Test Source"}
data={data}
columns={columns}
options={options}
/>
</Container>
</div>
);
}
export default TestSource;
I think the problem is that I have a global array and I try to push new data inside a function. Is there a way to work around this in? Appreciate any advise you could provide on this.
You could lift the state up to the parent component:
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import MUIDataTable from "mui-datatables";
import {
Button,
Select,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
TextField,
DialogActions
} from "#material-ui/core";
import "./styles.css";
function ModalBox(props) {
const [open, setOpen] = useState(false);
const [state, setState] = useState({
dname: "",
dsource: ""
});
const handleChange = name => e => {
setState({
...state,
[name]: e.target.value
});
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="contained" color="primary" onClick={handleClickOpen}>
Create New
</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="Name"
type="text"
value={state.dname || ""}
onChange={handleChange("dname")}
fullWidth
/>
<Select
native
fullWidth
value={state.dsource || ""}
onChange={handleChange("dsource")}
>
<option value="" />
<option value={"mssql"}>mssql</option>
<option value={"oracle"}>oracle</option>
</Select>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button
onClick={() => {
props.addDataSource(state.dname, state.dsource);
setOpen(false);
}}
color="primary"
>
Add
</Button>
</DialogActions>
</Dialog>
</div>
);
}
function App() {
const columns = ["Id", "Name", "Provider"];
const [data, setData] = useState([]);
let id = 0;
function createData(name, provider) {
id += 1;
return [id, name, provider];
}
useEffect(() => {
const data = [
createData("Dummy1", "oracle"),
createData("Dummy2", "mssql"),
createData("Dummy3", "oracle")
];
setData(data);
}, []);
const options = {
filterType: "checkbox"
};
const addDataSource = (dname, dsource) => {
const updated = [...data];
updated.push(createData(dname, dsource));
setData(updated);
};
return (
<div className="f-height fx-column-cont">
<div>
<ModalBox
addDataSource={(dname, dsource) => addDataSource(dname, dsource)}
/>
<MUIDataTable
title={"Test Source"}
data={data}
columns={columns}
options={options}
/>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I would also suggest to crate different files for the components and do some refactoring and cleanup :-) Hope that helps.