React钩子状态后退一步而不更新



我正在尝试创建一个平均计算器,我在下拉列表和文本字段上的OnChange事件会更新挂钩值,但当我试图获得最终值时,但要计算,我现在需要按两次按钮,否则它不会考虑挂钩中的最新状态,请让我知道我在这里缺少了什么,我曾尝试使用异步等待来完成,但没有任何好处。以下是代码

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormHelperText from '@material-ui/core/FormHelperText';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';


const useStyles = makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
width: '25ch',
},
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));

export default function AverageCalculator() {
const classes = useStyles();

const [price1, setPrice1] = React.useState(7500);
const [price2, setPrice2] = React.useState(7300);
const [price3, setPrice3] = React.useState(7200);
const [price4, setPrice4] = React.useState(0);

const [percentage1, setPercentage1] = React.useState(1);
const [percentage2, setPercentage2] = React.useState(2);
const [percentage3, setPercentage3] = React.useState(1);
const [percentage4, setPercentage4] = React.useState(1);

const [portfolioUsed, setportfolioUsed] = React.useState(0);
const [avgPrice, setAvgPrice] = React.useState(0);

const [update, setUpdate] = React.useState(true);

const handleApply = (event) => {
setUpdate(!update)
console.log("PPPPP", update)
}

React.useEffect(async() => {
await setportfolioUsed((price1 > 0 ? percentage1 : 0) + (price2 > 0 ? percentage2 : 0) + (price3 > 0 ? percentage3 : 0) + (price4 > 0 ? percentage4 : 0))
await setAvgPrice((price1 * percentage1 + price2 * percentage2 + price3 * percentage3 + price4 * percentage4) / portfolioUsed)
}, [update])

return (
<div>
<form className={classes.root} noValidate autoComplete="off">
<TextField id="price1" value={price1} label="Primary Buying" onChange={(e) => setPrice1(e.target.value)} />
<TextField
id="perc1"
select
label="Portfolio"
value={percentage1}
onChange={(e) => setPercentage1(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField id="price2" value={price2} label="Backup 1" onChange={(e) => setPrice2(e.target.value)} />
<TextField
id="perc2"
select
label="Portfolio"
value={percentage2}
onChange={(e) => setPercentage2(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField id="price3" value={price3} label="Backup 2" onChange={(e) => setPrice3(e.target.value)} />
<TextField
id="perc3"
select
label="Portfolio"
value={percentage3}
onChange={(e) => setPercentage3(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField id="price4" value={price4} label="Backup 3" onChange={(e) => setPrice4(e.target.value)} />
<TextField
id="perc4"
select
label="Portfolio"
value={percentage4}
onChange={(e) => setPercentage4(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<p>{portfolioUsed > 4 ? "Your portfolio percentage selection is wrong" : "Average Price: " + avgPrice}</p>

<Button variant="contained" color="primary" onClick={()=>setUpdate(prevState=>!prevState)}>
Apply
</Button>

</div>
);
}

检查您的useEffect函数。您正在传递一个异步函数,但由于useEffect的工作方式,您得到了一个竞争条件。在useEffect函数中进行异步操作的正确方法如下

React.useEffect(()=> {
const asyncAction = async() => {
await setportfolioUsed((price1 > 0 ? percentage1 : 0) + (price2 > 0 ? percentage2 : 0) + (price3 > 0 ? percentage3 : 0) + (price4 > 0 ? percentage4 : 0))
await setAvgPrice((price1 * percentage1 + price2 * percentage2 + price3 * percentage3 + price4 * percentage4) / portfolioUsed)
}


asyncAction();
}, [update])

如果你使用一个反应短绒,你会看到这样的警告:

"等待"对该表达式的类型没有影响。ts(80007(效果回调是同步的,以防止出现竞争情况。将异步功能放入:

在这里,您有一个链接,可以重现您的问题:不良反应使用效果

这是因为useState在本质上是异步的,但不能简单地await来获得sync行为。同样正如@rubandmatos1985所提到的,您不应该将asyn函数传递给useEffect

就修复而言,你可以简单地绕过这样的问题

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";
const useStyles = makeStyles((theme) => ({
root: {
"& .MuiTextField-root": {
margin: theme.spacing(1),
width: "25ch"
}
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function AverageCalculator() {
const classes = useStyles();
const [price1, setPrice1] = React.useState(7500);
const [price2, setPrice2] = React.useState(7300);
const [price3, setPrice3] = React.useState(7200);
const [price4, setPrice4] = React.useState(0);
const [percentage1, setPercentage1] = React.useState(1);
const [percentage2, setPercentage2] = React.useState(2);
const [percentage3, setPercentage3] = React.useState(1);
const [percentage4, setPercentage4] = React.useState(1);
const [portfolioUsed, setportfolioUsed] = React.useState(0);
const [avgPrice, setAvgPrice] = React.useState(0);
const handleApply = () => {
const newPortfolioUsed =
(price1 > 0 ? percentage1 : 0) +
(price2 > 0 ? percentage2 : 0) +
(price3 > 0 ? percentage3 : 0) +
(price4 > 0 ? percentage4 : 0);
setportfolioUsed(newPortfolioUsed);
setAvgPrice(
(price1 * percentage1 +
price2 * percentage2 +
price3 * percentage3 +
price4 * percentage4) /
newPortfolioUsed
);
};
return (
<div>
<form className={classes.root} noValidate autoComplete="off">
<TextField
id="price1"
value={price1}
label="Primary Buying"
onChange={(e) => setPrice1(e.target.value)}
/>
<TextField
id="perc1"
select
label="Portfolio"
value={percentage1}
onChange={(e) => setPercentage1(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField
id="price2"
value={price2}
label="Backup 1"
onChange={(e) => setPrice2(e.target.value)}
/>
<TextField
id="perc2"
select
label="Portfolio"
value={percentage2}
onChange={(e) => setPercentage2(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField
id="price3"
value={price3}
label="Backup 2"
onChange={(e) => setPrice3(e.target.value)}
/>
<TextField
id="perc3"
select
label="Portfolio"
value={percentage3}
onChange={(e) => setPercentage3(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<form className={classes.root} noValidate autoComplete="off">
<TextField
id="price4"
value={price4}
label="Backup 3"
onChange={(e) => setPrice4(e.target.value)}
/>
<TextField
id="perc4"
select
label="Portfolio"
value={percentage4}
onChange={(e) => setPercentage4(e.target.value)}
>
<MenuItem value={0}>0%</MenuItem>
<MenuItem value={1}>25%</MenuItem>
<MenuItem value={2}>50%</MenuItem>
<MenuItem value={3}>75%</MenuItem>
<MenuItem value={4}>100%</MenuItem>
</TextField>
</form>
<p>
{portfolioUsed > 4
? "Your portfolio percentage selection is wrong"
: "Average Price: " + avgPrice}
</p>
<Button variant="contained" color="primary" onClick={handleApply}>
Apply
</Button>
</div>
);
}

此外,我建议您尽量减少状态变量的数量。你本可以做这样的

const [prices, setPrices] = useState({});
const [percentages, setPercentages] = useState({});

像这样的处理程序

const handleChangePrice = (e) => {
const { name, value } = e.target;
setPrices({
...prices,
[name]: value
});
};
const handleChangePercentage = (e) => {
const { name, value } = e.target;
setPercentages({
...percentages,
[name]: value
});
};

和你的Input一样

<TextField
id="price1"
value={prices.price1}
label="Primary Buying"
onChange={handleChangePrice}
/>

最新更新