材质UI IconButton onClick可增加或减少值

时间:2020-03-25 15:14:24

标签: javascript arrays reactjs button material-ui

我正在尝试实现材质UI图标按钮onClick(),它将减小或增大像这样,表格中每个元素的>卡路里值。我的代码基于Table React component page code.

enter image description here

在这种情况下,如果单击 [-] 按钮,它将值为2,如果我单击[+]按钮,它将增加值为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>
  );
}

在2 IconButtons中,我尝试使用Calories来递减或递增onClick()的值,但是这样做的方式无效。我该怎么办?我想我需要使用组件statesetState()函数,但是我不知道如何分配它并获得该值。

3 个答案:

答案 0 :(得分:4)

是的,您必须在setState中使用onClick。在指出您所犯的错误后,我将告诉您该如何做。

您在这里犯的错误很少,

错误1:未将row保持在React状态。所有动态数据都必须存储为反应状态。

错误2: onClick不是函数,而是数字。 --row.Calorie不是函数,而是一个输出数字的表达式。

错误3:直接更改数据。在React和Functional Programming中,这是严格禁止的。您不应输入类似--row.Calorie的表达式。他们直接更改数据,而React无法跟踪更改。

进行这些调整,您就很好了。


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

))

答案 1 :(得分:0)

下一个代码完成工作。

您必须在下一个周期中进行思考: 当必须增加或减少以更新存储值时, 然后在row.map中显示 store 值。

这是了解反应如何工作的基本方法。

在这种情况下,您使用 Hooks 设置商店状态

我建议您学习 Redux

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


  const [calories, setCalories] = React.useState(rows);// set initial state is used only once

  console.log(calories);


  const onDecrement = key => () => {     
        setCalories( calories.map( (item, index) => item.name === key ? 
          {...item, calories: item.calories -1} : item));          
  };

  const onIncrement = key => () => {     
        setCalories( calories.map( (item, index) => item.name === key ? 
          {...item, calories: item.calories +1} : item));          
  };


  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>
          {calories.map(row => (
            <TableRow key={row.name}>
              <TableCell component="th" scope="row">
                {row.name}
              </TableCell>



              <TableCell align="right">
              <IconButton onClick={ onDecrement(row.name) }>
              <RemoveCircleOutlineRoundedIcon />
              </IconButton> 
              {row.calories} 
              <IconButton onClick={ onIncrement(row.name)  }>
              <AddCircleOutlineRoundedIcon />
              </IconButton>
              </TableCell>



              <TableCell align="right">{row.fat}</TableCell>

              <TableCell align="right">{row.carbs}</TableCell>

              <TableCell align="right">{row.protein}</TableCell>

            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

答案 2 :(得分:0)

React根据状态和属性来渲染组件,这使得渲染具有确定性(即,对于给定的输入,输出将始终相同),此条件允许做出反应以知道何时渲染页面。 因此,只要状态或道具发生更改,就会再次渲染组件以反映更改。

在您的情况下,onClick需要一个函数-事件处理程序。用户单击您的元素时,该元素应如何处理?用户需要将其定义为函数。

          <IconButton
            onClick={() => {
                --row.calories;
                alert("Decremented to " + row.calories);
              }}
            >
              <RemoveCircleOutlineRoundedIcon />
            </IconButton>
            {row.calories}
            <IconButton
              onClick={() => {
                ++row.calories;
                alert("Incremented to " + row.calories);
              }}
            >

所以我们添加了一个函数,我们可以看到该值准确地反映在警报中。但是该页面似乎无法反映当前的卡路里值。这是因为我们从未通知用户更改了某些内容。

这可以通过状态来完成,当我们知道某些数据可以被用户更改并且需要呈现时,我们可以选择使用状态。 国家属于一个组成部分。状态可以是具有单个字段的简单对象,也可以是具有多个字段和对象的复杂对象。

let [foodData, setFoodData] = useState(rows);
/* existing code */
<TableBody>
      {foodData.map((row, index) => (
/* existing code */
<IconButton
  onClick={() => {
  setFoodData(prev => [
  ...prev.slice(0, index),
  { ...prev[index], calories: prev[index].calories - 1 },
  ...prev.slice(index + 1)
  ]);
  }}
 >
/* do the same to increment */

'...'是ES6扩展运算符,为了标识要修改的当前行,我们使用'index'作为映射函数的参数。

现在要修改对象,我们可以创建一个包含以下内容的新数组:

  • 从0到索引的所有元素
  • 我们要修改的对象
  • 从索引+1到数组末尾的所有元素

数组的slice方法可以实现此目的,因为它不会修改原始数组。

此新数组将被设置为当前状态,反应将重新呈现。

https://www.newline.co/fullstack-react/-本书的第一章详细说明了这一点,并介绍了状态。 useState 是一个可以从此处引用的钩子-https://reactjs.org/docs/hooks-state.html