Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Material UI IconButton onClick to increment or decrement values

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.

enter image description here

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&nbsp;(g)</TableCell>
            <TableCell align="right">Carbs&nbsp;(g)</TableCell>
            <TableCell align="right">Protein&nbsp;(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.

like image 503
Grandtour Avatar asked Mar 25 '20 15:03

Grandtour


1 Answers

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>
  // ...

))

like image 125
Pushkin Avatar answered Nov 05 '22 06:11

Pushkin