我正在尝试创建一个平均计算器,我在下拉菜单和文本字段上的 OnChange 事件更新了挂钩值,但是当我尝试获取最终值时,但是要计算,我现在需要按 2 次按钮,否则它会不考虑钩子的最新状态,请让我知道我在这里缺少什么,我尝试使用 async await 但没有任何好处。以下是代码
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>
);
}
答案 0 :(得分:1)
检查您的 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])
如果你使用 react linter,你会看到这样的警告:
'await' 对这个表达式的类型没有影响。ts(80007) Effect 回调是同步的,以防止竞争条件。把异步函数放在里面:
此处提供了重现问题的链接: bad use of react useEffect
答案 1 :(得分:0)
这是因为 useState
本质上是异步的,但您不能简单地 await
获得 sync
行为。同样正如@rubendmatos1985 所提到的,您不应将 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}
/>