我正在尝试实现材质UI图标按钮的onClick()
,它将减小或增大像这样,表格中每个元素的>卡路里值。我的代码基于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 (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>
);
}
在2 IconButtons
中,我尝试使用Calories
来递减或递增onClick()
的值,但是这样做的方式无效。我该怎么办?我想我需要使用组件state
和setState()
函数,但是我不知道如何分配它并获得该值。
答案 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 (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (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'作为映射函数的参数。
现在要修改对象,我们可以创建一个包含以下内容的新数组:
数组的slice方法可以实现此目的,因为它不会修改原始数组。
此新数组将被设置为当前状态,反应将重新呈现。
https://www.newline.co/fullstack-react/-本书的第一章详细说明了这一点,并介绍了状态。 useState 是一个可以从此处引用的钩子-https://reactjs.org/docs/hooks-state.html