Reputation: 1741
I'm displaying a list of users in a table. Each row has a cell which contains a button to delete the user. When the delete button is clicked I'm rendering a Material-UI <Dialog />
component to confirm.
The issue is that the map is causing 3 dialogs to be rendered and the last one is the one on top. So If I have 3 users in the array, 3 dialogs are rendered but you can only interact with the 3rd dialog (it blocks the other 2 from view).
The issue is similar to this one.
Is there a way to control which dialog is opened?
handleDeleteUser
is an async function making a request so I need to pass id
and links
to that method.
I have a simplified codesandbox here. If you try to delete user 1 or user 2, you can see that only the id and link for user 3 appear in the console.
const handleDeleteUser = (id, links) => {
// this always prints the id and links for the last user in the table
console.log("deleting user -->", id);
console.log("user delete URL", links.self);
setOpen(false);
};
<Table.Body>
{userList && userList.length >= 1 ? (
userList.map(
({ id, attributes, links, i }, index) => {
return (
<Table.Row key={index}>
<Table.Cell textAlign="center">
<Button
circular
icon
size="mini"
color="red"
type="button"
onClick={handleClickOpen}
>
<Icon name="delete" />
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Confirm User Delete"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{`Are you sure you want to delete this user?`}
</DialogContentText>
</DialogContent>
<DialogActions>
<MUIButton
type="button"
onClick={handleDeleteUser.bind(
this,
id,
links
)}
color="primary"
>
Yes
</MUIButton>
<MUIButton
type="button"
onClick={handleClose}
color="primary"
autoFocus
>
Cancel
</MUIButton>
</DialogActions>
</Dialog>
Upvotes: 3
Views: 1876
Reputation: 156
Why did you render dialog in every loop? I think,you need to once define.Because your "open" state for dialog same every user and triggered whichever is clicked on.You can try;
const [ selectedUser,setSelectedUser ] = useState()
const [ open,setOpen ] = useState(false)
const handleDeleteUser = (id,links) => {
// this always prints the id and links for the last user in the table
console.log("deleting user -->", id);
console.log("user delete URL", links.self);
setOpen(false);
};
return(
<>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Confirm User Delete"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{`Are you sure you want to delete ${selectedUser.name} user?`}
</DialogContentText>
</DialogContent>
<DialogActions>
<MUIButton
type="button"
onClick={() => handleDeleteUser(selectedUser.id,selectedUser.links)}
color="primary"
>
Yes
</MUIButton>
<MUIButton
type="button"
onClick={handleClose}
color="primary"
autoFocus
>
Cancel
</MUIButton>
</DialogActions>
</Dialog>
{
users.map((user,index) => (
<TableRow style={{backgroundColor:'green'}} key={index}>
<TableCell style={{color:'#fff'}}>{user.name}</TableCell>
<Button onClick={() => {
selectedUser(user),
setOpen(true)
}} >Delete me!</Button>
</TableRow>
))
}
</>
)
Upvotes: 0
Reputation: 684
The problem you have is when the button to trigger the modal is pressed it would set the state open to true and all the Dialog shared this
what you do is move the Dialog component to a different file and import it from there and pass the click handlers and state down as props.
here is my solution
Move the button that triggers the modal open and the dialog component into a different file
import React, { useState } from "react";
import { Button, Icon } from "semantic-ui-react";
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";
import MUIButton from "@material-ui/core/Button";
const CustomDialog = (props) => {
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<>
<Button
circular
icon
size="mini"
color="red"
type="button"
onClick={() => handleClickOpen()}
>
<Icon name="delete" />
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Confirm User Delete"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{`Are you sure you want to delete this user: ${props.id}?`}
</DialogContentText>
</DialogContent>
<DialogActions>
<MUIButton
type="button"
onClick={() => {
props.handleDeleteUser(props.id, props.links);
setOpen(false);
}}
color="primary"
>
Yes
</MUIButton>
<MUIButton
type="button"
onClick={handleClose}
color="primary"
autoFocus
>
Cancel
</MUIButton>
</DialogActions>
</Dialog>
</>
);
};
export default CustomDialog;
I refactored your table component like this
<React.Fragment key={index}>
<Table.Row key={index}>
<Table.Cell textAlign="center">
<CustomDialog
id={id}
links={links}
handleDeleteUser={handleDeleteUser}
/>
</Table.Cell>
<Table.Cell>{id}</Table.Cell>
</Table.Row>
</React.Fragment>
as you see I have passed the id
and links
and handleDeleteUser
as props , by doing so you make your dialog have its own state rather than sharing the state hence you were not getting the result you wanted
you can preview it here codesandbox
It's better having every component on a different file and its way cleaner, you can easily debug problems that would arise
Upvotes: 5