Пользовательский интерфейс материала IconButton onClick для увеличения или уменьшения значений - PullRequest
6 голосов
/ 25 марта 2020

Я пытаюсь реализовать onClick() Материал пользовательского интерфейса IconButton , который будет уменьшать или увеличивать Калории Значение каждого элемента в таблице, как это. Мой код основан на коде страницы компонента Table React.

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(), но способ, которым я это делаю, не работает. Что мне делать? Я думаю, что мне нужно использовать компонент state и, следовательно, функцию setState(), но я не знаю, как назначить это и получить это значение.

Ответы [ 3 ]

4 голосов
/ 30 марта 2020

Да, вы должны использовать setState в onClick. Я скажу вам, как это сделать после того, как укажу на ошибки, которые вы сделали.

Здесь вы делаете несколько ошибок,

Ошибка 1: Не поддерживается row в состоянии реакции. Все данные динамического c должны быть сохранены как состояние реакции.

Ошибка 2: onClick не функция, а число. --row.Calorie это не функция, это выражение, которое выводит число.

Ошибка 3: Непосредственное искажение данных. Это строго запрещено в Реактивном и Функциональном Программировании. Вы не должны вводить выражения типа --row.Calorie. Они напрямую видоизменяют данные, и React не может отслеживать изменения.

Внесите эти изменения, и вы можете 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>
  // ...

))

0 голосов
/ 05 апреля 2020

React рендерит компонент как функцию состояния и реквизита, это делает детерминированность рендеринга c (т. Е. Для заданного ввода результат всегда будет одинаковым), этот критерий позволяет реагировать, чтобы знать, когда визуализировать страницу. Таким образом, всякий раз, когда изменяется состояние или реквизит, компонент отображается снова, чтобы отразить это изменение.

В вашем случае onClick ожидает функцию - обработчик события. Когда пользователь щелкает ваш элемент, как элемент должен его обрабатывать? Это должно быть определено пользователем как функция.

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

, поэтому мы добавили функцию и видим, что значение точно отражено в предупреждении. Но страница, кажется, не отражает текущее значение калории ie. Это потому, что мы никогда не информировали, что что-то изменилось пользователем.

Это может быть сделано через состояние, мы можем выбрать использование состояния, когда мы знаем, что некоторые данные могут быть изменены пользователем, и они должны быть отображены. Государство принадлежит компоненту. State может быть простым объектом с одним полем или сложным с несколькими полями и объектами.

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 до конца массива

Метод среза массива может достичь этого, поскольку он не измените исходный массив.

Этот новый массив будет установлен как текущее состояние, и реакция будет перерисована.

https://www.newline.co/fullstack-react/ - первая глава книги объясняет это подробно и вводит в состояние. useState - это хук, на который можно сослаться - https://reactjs.org/docs/hooks-state.html

0 голосов
/ 02 апреля 2020

Следующий код сделает работу.

Вы должны подумать в следующем цикле: когда приращение или уменьшение должно обновить значение магазина, то в row.map отобразится store value.

Это базовый c способ понять, как работают реакции.

В этом случае вы используете 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>
  );
}
...