I'm trying to implement the onClick()
of a Material UI IconButton, that will decrement or increment the Calories value of each element in the table, like this. My code is based on the Table React component page code.
In this case, if I click in the [ - ] button, it will decrement the value to 2, and if I click in the [ + ] button, it will increment the value to 4.
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import IconButton from '@material-ui/core/IconButton';
//Icons
import AddCircleOutlineRoundedIcon from '@material-ui/icons/AddCircleOutlineRounded';
import RemoveCircleOutlineRoundedIcon from '@material-ui/icons/RemoveCircleOutlineRounded';
/*------------------------- IMPORTS ---------------------------*/
const useStyles = makeStyles({
table: {
minWidth: 650,
},
});
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function DenseTable() {
const classes = useStyles();
return (
<TableContainer component={Paper}>
<Table className={classes.table} size="small" aria-label="a dense 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">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
/* ----------------- IconButton HERE ---------------- */
<TableCell align="right">
<IconButton onClick={ --row.calories }>
<RemoveCircleOutlineRoundedIcon />
</IconButton>
{row.calories}
<IconButton onClick={ ++row.calories }>
<AddCircleOutlineRoundedIcon />
</IconButton>
</TableCell>
/* ----------------- IconButton END ---------------- */
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
In the 2 IconButtons
, I'm trying to decrement or increment Calories
value using onClick()
, but the way I'm doing this isn't working. What should I do? I think I need to use the component state
and therefore, the setState()
function, but I've no idea how to assign this and get that value.
Yes, You must use setState
in onClick
. I'll tell you how to do it after I point out the mistakes you made.
There are few mistakes you're doing here,
Mistake 1: Not maintaining row
in a React State. All the dynamic data must be stored as a React State.
Mistake 2: onClick
isn't a function but a number. --row.Calorie
isn't a function, it's an expression which outputs number.
Mistake 3: Mutating the data directly. This is strictly prohibited in React and Functional Programming. You should not type expressions like --row.Calorie
. They directly mutate the data and React cannot track the changes.
Make these adjustments and you're good to go.
// Straight away create a row state like this.
const [rows, setRows] = useState([
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
])
// inside map function
rows.map((row, index) => (
// ...
<IconButton
// pass a function instead of expression
onClick={() => {
// NOTE: I'm using only index and prev in this block
// and not using row, or directly mutating them
setRows(prev => [
...prev.slice(0, index),
{ ...prev[index], calories: prev[index].calories - 1 },
...prev.slice(index + 1)
])
// Also NOTE: I'm creating a new state
// from the previous state - `prev` without
// mutating the `prev`
}}
>
<RemoveCircleOutlineRoundedIcon />
</IconButton>
// ...
))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With