【问题标题】:Material UI IconButton onClick to increment or decrement valuesMaterial UI IconButton onClick 增加或减少值
【发布时间】:2020-07-06 04:03:36
【问题描述】:

我正在尝试实现 Material UI IconButtononClick(),它将 减少增加 卡路里 表中每个元素的值,像这样。我的代码基于Table React component page code.

在这种情况下,如果我点击 [ - ] 按钮,它会将值递减为 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 中,我尝试使用 onClick() 减少或增加 Calories 的值,但我这样做的方式不起作用。我该怎么办?我想我需要使用组件state,因此需要使用setState()函数,但我不知道如何分配它并获得该值。

【问题讨论】:

    标签: javascript arrays reactjs button material-ui


    【解决方案1】:

    是的,您必须在onClick 中使用setState。在我指出你犯的错误之后,我会告诉你如何去做。

    你在这里犯的错误很少,

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

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

    错误3:直接变异数据。这在 React 和函数式编程中是严格禁止的。你不应该输入像--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>
      // ...
    
    ))
    
    

    【讨论】:

      【解决方案2】:

      下一个代码开始工作。

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

      这是了解 React 工作原理的基本方法。

      在这种情况下,您使用 Hooks 来设置 storestate

      我推荐你学习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>
        );
      }
      

      【讨论】:

        【解决方案3】:

        React 将组件渲染为 state 和 props 的函数,这使得渲染具有确定性(即对于给定的输入,输出将始终相同),此标准允许 react 知道何时渲染页面。 因此,每当 state 或 props 发生变化时,组件都会再次渲染以反映变化。

        在您的情况下,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' 作为 map 函数的参数。

        现在要修改对象,我们可以创建一个新数组,其中包含:

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

        Array 的 slice 方法可以做到这一点,因为它不会修改原始数组。

        这个新数组会被设置为当前状态,react 会重新渲染。

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

        【讨论】:

          猜你喜欢
          • 2020-05-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-18
          • 2017-06-03
          • 1970-01-01
          • 2021-11-21
          • 1970-01-01
          相关资源
          最近更新 更多