React Material Ui Dialog not Displaying Correct Values - javascript

I am trying to play with react material ui dialog boxes and I noticed a problem or maybe I am doing it wrong. I've an object a and when I click on the a button in list, it should display the respective id number but it is always displaying the id number of the last id,index instead, what is the issue? Is it because i am calling them in a loop and all three dialogue boxes are being called at the same time? what should I do to basically show the respective id with every button.
...
export default function AlertDialog() {
const [open, setOpen] = React.useState(false);
const a = [{ id: 1 }, { id: 2 }, { id: 3 }];
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<>
<List>
{a.map(({ id }, index) => {
return (
<>
<ListItem button onClick={handleClickOpen}>
{id}
</ListItem>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{id}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description" />
</DialogContent>
</Dialog>
</>
);
})}
</List>
</>
);
}
...
my sample https://codesandbox.io/s/material-demo-k5s8k?file=/demo.js

All 3 dialogs are being opened, because you are controlling all 3 of them using the same open variable. The last dialog is just the one on top. If you look at the DOM via the browser developer tools you will see that all 3 are there.
You can fix this by managing the open state in a way that allows you to tell which id is open.
One way is to set into state the id of the dialog that is open:
import React from "react";
import Dialog from "#material-ui/core/Dialog";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import { List, ListItem } from "#material-ui/core";
export default function AlertDialog() {
const [openId, setOpenId] = React.useState(null);
const a = [{ id: 1 }, { id: 2 }, { id: 3 }];
const handleClickOpen = id => {
setOpenId(id);
};
const handleClose = () => {
setOpenId(null);
};
return (
<>
<List>
{a.map(({ id }, index) => {
return (
<>
<ListItem button onClick={() => handleClickOpen(id)}>
{id}
</ListItem>
<Dialog
open={openId === id}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{id}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description" />
</DialogContent>
</Dialog>
</>
);
})}
</List>
</>
);
}

Related

How to clear state on Dialog close React?

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();
};

ReactJs mapping array of items, onClick button always returns the last item of the array

I have this component and I want to be able to call a function with a given item id, but the problem is that when I call cancelItem with given parameter, it always returns the id of the last item from the array. I think it has something to do with the scope, but I cannot think of a solution.
Edit 1: Uploaded a minimal reproducible example
import * as React from "react";
import {
Typography,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
ListItem,
ListItemText,
ListItemButton,
Box
} from "#mui/material";
interface IItem {
item_id: number;
name: string;
}
interface ItemEditModel {
itemId: number;
}
let items: IItem[] = [
{ item_id: 0, name: "first item" },
{ item_id: 1, name: "second item" },
{ item_id: 3, name: "third item" }
];
export default function App() {
const [open, setOpen] = React.useState(false);
const cancelItem = (item_id: number) => {
let newItem: ItemEditModel = { itemId: item_id };
console.log(newItem);
setOpen(false);
// do cancel item
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<>
{items.map((item: IItem, index: number) => (
<Box key={index}>
<ListItem>
<Typography>
{" "}
<b>Item {item.name}</b>
</Typography>
<ListItemButton onClick={handleClickOpen}>
<ListItemText primary="Cancel" />
</ListItemButton>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Cancel</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={handleClose}>
No
</Button>
<Button
variant="contained"
onClick={() => cancelItem(item.item_id)}
autoFocus
>
Yes
</Button>
</DialogActions>
</Dialog>
<ListItemButton onClick={() => console.log(item.item_id)}>
<ListItemText primary="Edit" />
</ListItemButton>
</ListItem>
</Box>
))}
</>
);
}
Here is a link to a sandbox.
It's not the item ID, it's the dialog. Every click of a button opens all three dialogs. You're just interacting with the last, top-most one.
There are multiple dialogs, but only one state indicating whether they are all open or closed:
const [open, setOpen] = React.useState(false);
One approach could be to use the item ID as the dialog state. So start it as undefined:
const [open, setOpen] = React.useState();
And each button can set the state to that item's ID:
<ListItemButton onClick={() => handleClickOpen(item.item_id)}>
and:
const handleClickOpen = (id) => {
setOpen(id);
};
const handleClose = () => {
setOpen(undefined);
};
Then check if the ID matches to determine if the dialog is open:
open={open === item.item_id}
Alternatively, with a little more refactoring you can remove the dialog component from the map entirely and have just one dialog overall. You'd then need to track the state of which record is "currently selected".
Either way, the overall goal is to only display one dialog at a time.

How to show modal based on what was clicked on the card [duplicate]

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>

Create a dropdown that will appear after clicking on chip component that is within TextField

I am having trouble trying to implement a customized dropdown that does not seem to be a built in feature in Material UI. I am using Material UI for all these components btw. So I have a TextField with a Chip for the End Adornment.
The expected behavior is that if the user were to click on the chip, there should be a dropdown that pops up under the TextField where the user can select the type of vehicle. However, I don't think there is a built in Material UI way to do this. Any suggestions? Any suggestions would be appreciated. Thanks!
You can implement it like this:
https://codesandbox.io/s/hungry-golick-kdoylz?file=/src/App.js
import { useState } from "react";
import { TextField, Chip, InputAdornment, Menu, MenuItem } from "#mui/material";
import KeyboardArrowDown from "#mui/icons-material/KeyboardArrowDown";
export default function App() {
const [selectedItem, setSelectedItem] = useState("Jeep");
return (
<TextField
label="With normal TextField"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<ChipDropDown
items={["Jeep", "Volvo", "Ferrari"]}
selectedItem={selectedItem}
onChanged={setSelectedItem}
/>
</InputAdornment>
)
}}
variant="filled"
/>
);
}
const ChipDropDown = ({ items, selectedItem, onChanged }) => {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (item) => {
onChanged(item);
setAnchorEl(null);
};
return (
<div>
<Chip
label={selectedItem}
onClick={(e) => setAnchorEl(e.currentTarget)}
onDelete={(e) => e}
deleteIcon={<KeyboardArrowDown />}
/>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={(e) => setAnchorEl(null)}
>
{items.map((item) => (
<MenuItem key={item} onClick={(e) => handleClick(item)}>
{item}
</MenuItem>
))}
</Menu>
</div>
);
};

React sidebar dropdown menu is overlapping with options

I am working on this sidebar for a project. I am facing a problem.
Required Behavior
If I click on an option which has a submenu, it will show the submenu below the clicked option. And it will push down the other options below. e.g this sidebar https://med.stanford.edu/ultrasound/research.html
Current Behavior
If I click on an option, it overlaps the below options.
How to fix this?
My sidebar Component
import { useState } from "react";
import { Box, Text } from "#chakra-ui/react";
import sidebarItems from "./sidebarItems";
export default function Sidebar() {
const [selectedSubMenu, setSelectedSubMenu] = useState("");
const [isOpen, setIsOpen] = useState(false);
const handleClick = (title) => {
setSelectedSubMenu(title);
setIsOpen(!isOpen);
};
return (
<div>
<Box>
{sidebarItems.map((items) => {
return (
<Box
width='200px'
height='40px'
textAlign='start'
cursor='pointer'
onClick={() => handleClick(items.title)}
fontFamily='Fjord One'
boxShadow='lg'
_hover={{
bgColor: "#1a2963",
color: "white",
}}
>
<Text fontSize='xl' as='bold'>
{items.title}
</Text>
{isOpen && items.title === selectedSubMenu
? items.subMenu?.map((item) => {
return (
<Box
bgColor='#e4e8e5'
boxShadow='md'
textAlign='start'
width='200px'
height='40px'
>
<Text fontFamily='Fjord One' fontSize='xl'>
{item.title}{" "}
</Text>
</Box>
);
})
: null}
</Box>
);
})}
</Box>
</div>
);
}

Categories

Resources