trying to map data inside of already mapped data - javascript

data set 1 is an array, that contains another array data set 2. Data set 1 is currently mapped over to display a single column table with data1.name inside of it. data1.name is a button that can be clicked to display related data2.data. The current problem I am facing is that data sets that are unrelated to their respective parents are displaying in all the parent data locations. For instance, clicking data1[a] will display all the data2.data from data1[b] and so on and so forth. When i input a new data1, with new data2 nested. The data that is previously rendered in all the data1 sets is replaced by this new data.
I've attached my code in hopes that someone may be able to shed some light. Please beware that I am using an MUI library so the code is a bit bulky. There is a table, that maps over data1 to display each data1.name and then data.2 is mapped relatively immediately after presenting the user with a modal display of the data1.data2.data.
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.hover,
},
// hide last border
'&:last-child td, &:last-child th': {
border: 0,
},
}));
const Reports = () => {
//GRAB THE USER PROFILE
const [users, setUsers] = useState({ months: [] })
useEffect(() => {
axios.get('/api/users/profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}
)
.then(res => {
console.log(res.data)
setUsers({...users, months: res.data.months })
console.log(users.months)
})
}, [])
// defining modal styles
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 1000,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
// defining modal state
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
<NavBar></NavBar>
<br></br> <br></br>
<h1 style={{ color: "white", textAlign: "center", fontSize: "50px" }}>Budget Summaries</h1>
<br></br> <br></br>
<Container>
<Grid container spacing={2}>
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Month</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{users.months.map(month => (
<StyledTableRow key={month._id}>
<StyledTableCell component="th" scope="row">
<Button onClick={handleOpen}>{month.name}</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
<Container>
< Grid container spacing={2} >
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Categories</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Spent</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Goals</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Results</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right"></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{month.categories.map(category => (
<StyledTableRow key={category.name}>
<StyledTableCell component="th" scope="row">
{category.name}
</StyledTableCell>
<StyledTableCell align="right">{category.actualValue}</StyledTableCell>
<StyledTableCell align="right">{category.goalValue}</StyledTableCell>
<StyledTableCell align="right">{category.result}</StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid >
</Container >
</Typography>
</Box>
</Modal>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid>
</Container>
<br></br><br></br>
<hr style={{ color: "white" }}></hr>
<Footer></Footer>
</>
)
}
export default Reports

Please try replacing below code snippets as suggested:
1.
// defining modal state
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
with
// defining modal state
const [open, setOpen] = React.useState({});
const handleOpen = monthId => setOpen(prev => ({...prev, [monthId]: true}));
const handleClose = monthId => setOpen(prev => ({...prev, [monthId]: false}));
<Button onClick={handleOpen}>{month.name}</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
with
<Button onClick={() => handleOpen(month._id)}>{month.name}</Button>
<Modal
open={open[month._id]}
onClose={() => handleClose(month._id)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
Explanation
The current code has only one state-variable named open which gets set to true any time user clicks on the month.name button.
When it is set to true, all of the modals which have <Modal open={open}... will be rendered/displayed
The changes suggested above will make open an object of the form { monthId1: false, monthId2: true, ......}
Changes to handleOpen and handleClose will ensure only a particular month's open/close flag is updated
Replacing <Modal open={open} .... with <Modal open={open[month._id]} ...., ensures that each modal uses the true/false specific to the month._id.
NOTE: If month._id may be duplicated, then use a different unique attribute. If no such attribute exists, then replace {users.months.map(month => ( with {users.months.map((month, monthIndex) => ( and use monthIndex.
Observation / Note
We may avoid handleOpen, handleClose methods by using a simple flip method like so:
const flip = monthId => setOpen(prev => ({...prev, [monthId]: !prev[monthId]}));
So, both onClick and onClose will become something like so:
{() => flip(month._id)}

Related

how to bind data in material-ui using redux hooks

I am using material-UI and redux in react I am using first-time redux so I am confused about how to implement it with my list items.
here is my code of product.js and I am successfully getting data in my redux store, I will show you below
const dispatch = useDispatch();
const {loading, product, error, productCount} = useSelector(state => state.products);
console.log(loading)
console.log(product)
console.log(error)
console.log(productCount)
useEffect(()=>{
const data = dispatch(getProducts());
console.log(data)
},[dispatch])
here is my code of table
<TableContainer component={Paper}>
<Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
<TableBody>
{(rowsPerPage > 0
? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: rows
).map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell style={{ width: 160 }} align="right">
{row.calories}
</TableCell>
<TableCell style={{ width: 160 }} align="right">
{row.fat}
</TableCell>
</TableRow>
))}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
colSpan={3}
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: {
'aria-label': 'rows per page',
},
native: true,
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
if I show to my redux data so I am receiving data on this page
here is my console image
I hope it will be enough code to describe my problem but if anyone want to see further code to understand my problem so i will provide it

Pulling data out of a mapped array to calculate sums in react

I have 2 data sets that are currently being mapped over. the second data set is being mapped over inside of the first data set and rendering the values onto a modal for the user to view. I am however trying to take this second data set that is being mapped over out of its map sequence to render a sum total of each associated data point. For instance, I have a 'goalValue', an 'actualValue', and a 'result' value each of which belong to a specified category.
I should like to grab these data points when they are mapped over and push them out of the map into a separate array and or state to calculate a total that can be rendered at the bottom of the table in the modal itself. I created a separate component called ResultCalculator that takes in each value within the 'category.' Currently the state that i am trying to push the mapped values to is causing an infinite loop. I thought that maybe i could use a 'useEffect' but that isn't possible inside of a callback function.
I have added the code below for reference. Any potential insight would be greatly appreciated. I have been working on this for the last few hours whilst gaining no headway. Any search I've typed in on google has yet to render any valuable response.
Here is the code for the mappings.
const Reports = () => {
// declare const for grabbing user profile
const userProfile = () => {
axios.get('/api/users/profile', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}
)
.then(res => {
console.log(res.data)
setUsers({ ...users, months: res.data.months })
})
}
// BUTTON TO DELETE A MONTH
const handleDeleteMonth = (id) => {
console.log(id)
axios.delete(`/api/months/${id}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('user')}`
}
}).then(res => {
console.log(res)
userProfile()
})
}
//GRAB THE USER PROFILE
const [users, setUsers] = useState({ months: [] })
useEffect(() => {
userProfile()
}, [])
// defining modal styles
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 1000,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
// defining modal state
const [open, setOpen] = React.useState({});
const handleOpen = monthId => setOpen(prev => ({ ...prev, [monthId]: true }));
const handleClose = monthId => setOpen(prev => ({ ...prev, [monthId]: false }));
//RESULT CALCULATION STATES
const [actualsArray, setActualsArray] = useState(0)
// const [goalsResult, setGoalsResult] = useState(0)
// const [resultsResult, setResultsResult] = useState(0)
return (
<>
<NavBar></NavBar>
<br></br> <br></br>
<h1 style={{ color: "white", textAlign: "center", fontSize: "50px" }}>Budget Summaries</h1>
<br></br> <br></br>
<Container>
<Grid container spacing={2}>
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Month</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{users.months.map(month => (
<StyledTableRow key={month._id}>
<StyledTableCell component="th" scope="row">
<Button onClick={() => handleOpen(month._id)} align="left">{month.name}</Button>
<Button style={{ minWidth: 200 }} align="right" onClick={() => {handleDeleteMonth(month._id)}}>Delete</Button>
<Modal
open={open[month._id]}
onClose={() => handleClose(month._id)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography id="modal-modal-title" variant="h6" component="h2">
<Container>
< Grid container spacing={2} >
<Grid item xs={0} md={1}>
</Grid>
<Grid item xs={12} md={10}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 700 }} aria-label="customized table">
<TableHead>
<TableRow>
<StyledTableCell style={{ fontSize: "25px" }}>Categories</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Spent</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Goals</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right">Results</StyledTableCell>
<StyledTableCell style={{ fontSize: "25px" }} align="right"></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{month.categories.map(category => (
<>
<StyledTableRow key={category.name}>
<StyledTableCell component="th" scope="row">
{category.name}
</StyledTableCell>
<StyledTableCell align="right">{category.actualValue}</StyledTableCell>
<StyledTableCell align="right">{category.goalValue}</StyledTableCell>
<StyledTableCell align="right">{category.result}</StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
</>
))}
<StyledTableRow>
<styledTableCell component="th" scope="row">Net</styledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"><ResultCalculator/></StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
</StyledTableRow>
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid >
</Container >
</Typography>
</Box>
</Modal>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
<Grid item xs={0} md={1}>
</Grid>
</Grid>
</Container>
<br></br><br></br>
<hr style={{ color: "white" }}></hr>
<Footer></Footer>
</>
)
}
I also will include the exterior component called ResultCalculator. There are some things that are commented out because I am currently just trying to get 1 value to calculate correctly before trying the other 2.
> function ResultCalculator(actualValue, goalValue, result) {
>
> const [actualsResult, setActualsResult] = useState(0)
// const [goalsResult, setGoalsResult] = useState(0)
// const [resultsResult,setResultsResult] = useState(0)
>
> let actuals = 0
// let goals = 0
// let results = 0
>
> const actualCalculator = (actualValue) => {
> actuals = actuals + actualValue }
>
> // const goalCalculator = (goalValue) => {
// goals = goals + goalValue
// return goals
// }
>
> // const resultCalculator = (result) => {
// results = results + result
// return results
// }
actualCalculator(actualValue)
>
> // goalCalculator()
// resultCalculator()
useEffect(() => {
> setActualsResult(actuals)
}, [])
>
> // setGoalsResult(goals)
// setResultsResult(results)
>
> return (
> <>
> {actualsResult}
> {/* {goalsResult}
> {resultsResult} */}
> </> )
>
> }

How do I map over data from API in a collapsible table?

I don't know if the question captures what I had in my but I will explain below...
I fetched data from API and mapped into a collapsible table. Full details of the data should be embedded in EACH row such that onclick on each row, reveals the full details. Here's the code below
function Encounter() {
const [open2, setOpen2] = useState(false);
const [details, setDetails] = useState([]);
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
setDetails(fetch.data.data)
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getDetails();
}, []);
return (
<Wrapper>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell/>
<TableCell >
Enrollment ID
</TableCell>
<TableCell >
Encounter
</TableCell>
<TableCell>
Facility Code
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{details.map((detail, idx) => {
return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell key={idx}>
{detail.enrollment_id}
</TableCell>
<TableCell key={idx}>
{detail.encounter}
</TableCell>
<TableCell key={idx}>
{detail.faciity_code}
</TableCell>
</TableRow>
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0 }}
colSpan={6}
>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
Details
</Typography>
<Tooltip />
</Box>
</Collapse>
</TableCell>
</TableRow>
</>
);
})}
</TableBody>
</Table>
</Wrapper>
);
}
export default Encounter;
The problem I have is how to implement the open and setOpen state to individual row, also the Tooltip component(which is a the full table details from the API) to display full details of each row onclick which should correspond to the selected row in question.
Here are two solutions to the first problem.
1. Create a separate component for each <TableRow />
This component will have its own state and allows you to collapse/expand each row individually.
2. Use a dictionary for the open state
Since you have multiple (dynamic) rows, you can introduce a dictionary for the open state.
const [open, setOpen] = useState({});
For each row, you will use the open[idx] property to determine if the row should be "open"
<Collapse in={open[idx]} timeout="auto" unmountOnExit>
And in the <IconButton /> component, set the state based on the current row state.
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(current => ({ ...current, [idx]: !current[idx] }))}
>
Firstly add a variable in your details object:
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
const response = fetch.data.data;
response.map((elem) => elem.open = false)
setDetails(response)
} catch (err) {
console.log(err);
}
};
Then you can change the open variable for each element in details:
<IconButton
aria-label="expand row"
size="small"
onClick={() => detail.open = !detail.open)}
>
You might need to update the state, so change your onClick:
onClick={() => changeOpenStatus(idx))}
and the function:
const changeOpenStatus = (idx) => {
const newDetails = {...details}
newDetails[idx].open = !newDetails[idx].open;
setDetails(newDetails)
}

Material ui : Table scroll to top of new page

I'm a bit lost on this.
I already try many settings with scrollTop.
Let me explain, I'm using material Ui and their pagination stuff with table documentation here
so I'm stuck when I click on next set of row( or change page ), I start at the bottom. but I would like to start at the top of every new row.
If someone can help me and give me an explanation of why, A huge thanks!
sorry, I'm quite new to React.
here is my code :
function DisplayList(props) {
var rows = [];
const data = props.data;
const searchData = props.searchData;
const setHoverAddress = props.setHoverAddress;
const classes = useStyles1();
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
data.map((result, index) => { // WARNING : slice here which limits the number of results: .slice(0, 5)
const volulme = Math.round(result.volulme);
const volulme2 = Math.round(result.volulme2);
rows.push(
<div id={index}>
<ListItem
alignItems="flex-start"
onMouseEnter={e => {
console.log(index);
}}
>
<Grid container direction="row" spacing={1}>
<Grid item xs={5}>
{/* <Stage width={150} height={150}>
<Layer>
<Shape
sceneFunc={(context, shape) => {
context.beginPath();
context.moveTo(20, 10);
context.lineTo(120, 80);
context.lineTo(120, 140);
context.lineTo(22, 140);
context.closePath();
// (!) Konva specific method, it is very important
context.fillStrokeShape(shape);
}}
fill="#00D2FF"
stroke="black"
strokeWidth={2}
/>
</Layer>
</Stage> */}
</Grid>
<Grid item xs={7}>
<ListItemText
primary={
}
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
display="inline"
color="textPrimary"
>
Solid2 : {volulme2}
</Typography>
</React.Fragment>
}
/>
<ListItemText
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
display="inline"
color="textPrimary"
>
Solid : {volulme}
</Typography>
</React.Fragment>
}
/>
<FormControlLabel
control={
<Checkbox icon={<FavoriteBorder />}
checkedIcon={<Favorite />}
color="primary"
onClick={(e) => {
if (e.target.checked) {
addFavourite(parc_id, 1)
} else {
removeFavourite(parc_id, 1)
}
}}
name="checkedH" />
}
label="Enregistrer"
/>
</Grid>
</Grid>
</ListItem>
</div>
)
})
return (
<Table className={classes.table} aria-label="custom pagination table">
<TableBody>
{(rowsPerPage > 0
? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: rows
).map((row) => (
<TableRow key={index}>
<TableCell component="th" scope="row">
{row}
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
colSpan={3}
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
SelectProps={{
inputProps: { 'aria-label': 'rows per page' },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
)
}
Have you tried scrollIntoView?
I used scrollTo not working but scrollIntoView is fine.
const handleChangeRowsPerPage = (event) => {
tableRef.current && tableRef.current.scrollIntoView();
setRowsPerPage(parseInt(event.target.value, 10))
setPage(0)
}
I don't see scrollTop in your example, so it's tough to say exactly what the issue is. If you just trying to scroll the window try window.scrollTo(0, 0);
https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo
If your trying to scroll the table element itself you can use a ref.
To the top of your component add:
function DisplayList(props) {
const tableRef = React.createRef();
var rows = [];
const data = props.data;
...
Add the ref to your table:
return (
<Table
ref={tableRef}
className={classes.table}
aria-label="custom pagination table"
>
<TableBody>
...
Finally your page event use the ref to change scrollTop
const handleChangePage = (event, newPage) => {
setPage(newPage);
tableRef.current?.scrollTop = 0;
};
Оn Angular Material i did like this
// html
<table mat-table [dataSource]="dataSource" #dataTable>
...
</table>
// ts
#ViewChild('dataTable') dataTable: ElementRef;
pageChange(event: { pageSize: number; pageIndex: number; }): void {
const tableElement = this.dataTable['_elementRef']?.nativeElement;
tableElement?.scrollIntoView();
// set current page event.pageIndex or event.pageIndex + 1
// get data
}

Embedding a Menu inside a TableCell with Material-UI

I would like to implement Google's Material UI Menu Item inside of a TableCell, as shown in their docs here, as seen below:
Here is my current approach:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import {
Grid,
IconButton,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Menu,
MenuItem,
Button,
} from '#material-ui/core';
import { ExpandLess, ExpandMore } from '#material-ui/icons';
const styles = theme => ({});
const Questions = ({ data, classes, openMenu, anchorEls, handleClose }) => {
const CustomTableCell = withStyles(theme => ({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
body: {
fontSize: 14,
},
}))(TableCell);
const formatData = rawData => Object.keys(rawData).map(key => rawData[key]);
const n = { key: 'hi', rating: 55, text: 'wassup' };
return (
<Grid container alignItems={'center'} direction={'column'} spacing={8}>
<Paper className={classes.root}>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu('dude', e)}
>
<ExpandMore />
</Button>
<Menu
id={`dude`}
key="menu"
anchorEl={anchorEls.dude}
open={Boolean(anchorEls.dude)}
onClose={e => handleClose('dude', e)}
>
<MenuItem onClick={e => handleClose('dude', e)}>Delete</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>Flag</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>
Mark Answered
</MenuItem>
</Menu>
<Table className={classes.table}>
<TableHead>
<TableRow>
<CustomTableCell>Question</CustomTableCell>
<CustomTableCell numeric>Rating</CustomTableCell>
<CustomTableCell>Upvote</CustomTableCell>
<CustomTableCell>Downvote</CustomTableCell>
<CustomTableCell>Options</CustomTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow className={classes.row} key={n.key}>
<CustomTableCell component="th" scope="row">
{n.text}
</CustomTableCell>
<CustomTableCell numeric>{n.rating}</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandLess />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandMore />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu(n.key, e)}
>
<ExpandMore />
</Button>
<Menu
id={`simple-menu-${n.key}`}
key="menu"
anchorEl={anchorEls[n.key]}
open={Boolean(anchorEls[n.key])}
onClose={e => handleClose(n.key, e)}
>
<MenuItem onClick={e => handleClose(n.key, e)}>
Delete
</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>dude</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>choc</MenuItem>
</Menu>
</CustomTableCell>
</TableRow>
</TableBody>
</Table>
</Paper>
</Grid>
);
};
Questions.propTypes = {
data: PropTypes.object.isRequired,
anchorEls: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
openMenu: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired,
};
Questions.defaultProps = {};
export default withStyles(styles)(Questions);
This is working, however the menu is not appearing in the correct place, even when passing in the related event e. I put in a dummy element before the table to test whether or not the table was affecting the menu, and found that the dummy worked just fine, as you can see in the screenshot below.
And the improperly placed menu from the button in the table:
Any ideas on what could be happening? Obviously, the context and anchorEl isn't correctly identifying the location on the page, but not sure what to do to combat that.
Problem in your openMenu function.You need to set the event target in your openMenu function, and set null in handleClose function. I am giving here a little example which may help you:
class DemoList extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
anchorEls: []
}
}
handleActionClick = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = event.target;
this.setState({ anchorEls });
}
handleActionClose = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = null;
this.setState({ anchorEls });
}
render() {
let { classes } = this.props;
const { data, anchorEls } = this.state;
return (
<Paper className="main">
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map(row => (
<TableRow key={row.id}>
<TableCell component="th" scope="row"> {row.name} </TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={e => this.handleActionClick(row.id, e)}
>
<MoreVert />
</IconButton>
<Menu
id={row.id}
anchorEl={anchorEls[row.id]}
keepMounted
open={Boolean(this.state.anchorEls[row.id])}
onClose={e => this.handleActionClose(row.id, e)}
>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> View Details </MenuItem>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> Assign </MenuItem>
</Menu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
)
}
}
After hacking away I found that removing the CustomTableCell declaration and changing all the CustomTableCells to normal TableCells resolved the problem. 😫 Not sure why that would fix it given that the CustomTableCell code was pulled straight from the Demos page.

Categories

Resources