【发布时间】:2021-08-27 20:45:46
【问题描述】:
我有一个点赞按钮,点击后会将默认值setLikedNumbers(likedNumbers + 1); 加一。当再次按下按钮时,使用setLikedNumbers(likedNumbers - 1); 将其减一。这可以正常工作,直到每秒多次按下按钮,这会产生一些奇怪的值。 React 严格模式标签被移除。
问题视频:https://vimeo.com/593482477 一开始我慢慢地点击按钮,然后我每秒多次执行,然后 axios 赶上请求。
即使按钮在axios返回错误码并返回当前值-1之前多次递增,那不应该是因为增加的次数等于减少的次数而保留了原始值吗?
我怀疑问题所在的代码(我删除了一些不需要的行):
组件
<ToggleIcon
on={liked}
onIcon={
<FavoriteOutlinedIcon onClick={() => unlike(props.idData)} />
}
offIcon={
<FavoriteBorderIcon onClick={() => like(props.idData)} />
}
/>
JavaScript
const like = async (id) => {
await setLiked(true);
await setLikedNumbers(likedNumbers + 1);
axios
.request({
method: "POST",
url: `http://localhost:5000/like`,
headers: {
jwt: localStorage.getItem("jwt"),
},
data: {
place_id: id,
},
})
.catch(async (err) => {
await setLiked(false);
await setLikedNumbers(likedNumbers - 1);
});
};
const unlike = async (id) => {
await setLiked(false);
await setLikedNumbers(likedNumbers - 1);
axios
.request({
method: "POST",
url: `http://localhost:5000/unlike`,
headers: {
jwt: localStorage.getItem("jwt"),
},
data: {
place_id: id,
},
})
.catch(async (err) => {
await setLiked(true);
await setLikedNumbers(likedNumbers + 1);
};
整个代码
import React from "react";
import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
import Box from "@material-ui/core/Box";
import Card from "@material-ui/core/Card";
import CardActionArea from "@material-ui/core/CardActionArea";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import CardMedia from "@material-ui/core/CardMedia";
import Typography from "@material-ui/core/Typography";
import FavoriteBorderIcon from "@material-ui/icons/FavoriteBorder";
import BookmarkBorderIcon from "@material-ui/icons/BookmarkBorder";
import Dialog from "@material-ui/core/Dialog";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import DialogActions from "@material-ui/core/DialogActions";
import { Carousel } from "react-responsive-carousel";
import ReportOutlinedIcon from "@material-ui/icons/ReportOutlined";
import ShareOutlinedIcon from "@material-ui/icons/ShareOutlined";
import FavoriteOutlinedIcon from "@material-ui/icons/FavoriteOutlined";
import BookmarkOutlinedIcon from "@material-ui/icons/BookmarkOutlined";
import ToggleIcon from "material-ui-toggle-icon";
const axios = require("axios");
const CardElement = (props) => {
const [open, setOpen] = React.useState(false);
const [liked, setLiked] = React.useState(props.liked);
const [saved, setSaved] = React.useState(props.saved);
const [likedNumbers, setLikedNumbers] = React.useState(props.numbersLiked);
const handleClickOpen = () => {
setOpen(true);
};
const like = async (id) => {
await setLiked(true);
await setLikedNumbers(likedNumbers + 1);
axios
.request({
method: "POST",
url: `http://localhost:5000/like`,
headers: {
jwt: localStorage.getItem("jwt"),
},
data: {
place_id: id,
},
})
.catch(async (err) => {
await setLiked(false);
await setLikedNumbers(likedNumbers - 1);
});
};
const unlike = async (id) => {
await setLiked(false);
await setLikedNumbers(likedNumbers - 1);
axios
.request({
method: "POST",
url: `http://localhost:5000/unlike`,
headers: {
jwt: localStorage.getItem("jwt"),
},
data: {
place_id: id,
},
})
.catch(async (err) => {
await setLiked(true);
await setLikedNumbers(likedNumbers + 1);
};
const save = (id) => {
setSaved(true);
axios
.request({
method: "POST",
url: `http://localhost:5000/save`,
headers: {
jwt: localStorage.getItem("jwt"),
},
data: {
place_id: id,
},
})
.catch((err) => {
setSaved(false);
});
};
return (
<div>
<Card className="card">
<CardActionArea
onClick={() => {
handleClickOpen();
}}
>
{props.mainImg ? (
<CardMedia
className="mediaImgOverview"
image={"http://localhost:5000/image/" + props.mainImg}
/>
) : (
""
)}
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{props.title}
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
{props.description.length > 45
? props.description.substring(0, 45) + "..."
: props.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions className="ButtonHolder">
<Box className="likesContainer">
{props.likeButtonVisible ? (
<ToggleIcon
on={liked}
onIcon={
<FavoriteOutlinedIcon onClick={() => unlike(props.idData)} />
}
offIcon={
<FavoriteBorderIcon onClick={() => like(props.idData)} />
}
/>
) : (
""
)}
<Typography
style={{ marginLeft: props.likeButtonVisible ? 0 : "0.2vmax" }}
>
{likedNumbers}
</Typography>
</Box>
{props.likeButtonVisible ? (
<ToggleIcon
on={saved}
onIcon={
<BookmarkOutlinedIcon onClick={() => unsave(props.idData)} />
}
offIcon={
<BookmarkBorderIcon onClick={() => save(props.idData)} />
}
/>
) : (
""
)}
</CardActions>
</Card>
<Dialog
maxWidth="md"
onClose={handleClose}
aria-labelledby="MoreInfo"
open={open}
>
<DialogTitle id="MoreInfo" onClose={handleClose}>
{props.title}
</DialogTitle>
<DialogContent dividers>
{props.images[0].url ? (
<Carousel infiniteLoop="true">
{props.images.map((el) => {
return (
<div key={Math.random()}>
<img alt="" src={"http://localhost:5000/image/" + el.url} />
<p className="legend">{el.caption}</p>
</div>
);
})}
</Carousel>
) : (
""
)}
<Typography gutterBottom>
<Typography>
<b>Категория: </b>
{category(props.category)}
<b> Опасно: </b>
{dangerous(props.dangerous)}
<b> Цена: </b>
{price(props.price)} <b> Достъпност: </b>
{accessibility(props.accessibility)}
</Typography>
{props.description}
</Typography>
</DialogContent>
<DialogActions className="btnCard">
<Box className="likesContainer">
{props.likeButtonVisible ? (
<ToggleIcon
on={liked}
onIcon={
<FavoriteOutlinedIcon onClick={() => unlike(props.idData)} />
}
offIcon={
<FavoriteBorderIcon onClick={() => like(props.idData)} />
}
/>
) : (
""
)}
<Typography
style={{ marginLeft: props.likeButtonVisible ? 0 : "0.2vmax" }}
>
{likedNumbers == 0
? "Няма харесвания"
: likedNumbers == 1
? "1 харесване"
: likedNumbers + " харесвания"}
</Typography>
</Box>
<Box>
{props.likeButtonVisible ? (
<ToggleIcon
on={saved}
onIcon={
<BookmarkOutlinedIcon onClick={() => unsave(props.idData)} />
}
offIcon={
<BookmarkBorderIcon onClick={() => save(props.idData)} />
}
/>
) : (
""
)}
<ShareOutlinedIcon />
{props.reportButtonVisible ? <ReportOutlinedIcon /> : ""}
</Box>
</DialogActions>
</Dialog>
</div>
);
};
export default CardElement;
更新 1
经过几次实验,我相当确定问题是按钮(动画)的缓慢变化导致触发其他功能,例如喜欢而不是不喜欢,反之亦然。我正在尝试找到解决此问题的方法。
更新 2 通过创建一个封装函数来解决问题,该函数调用正确的类似/不类似函数,而不管存在哪个按钮。
【问题讨论】:
-
您需要将回调传递给
setState才能访问当前状态。setLikedNumbers(prev => prev + 1)你也不能awaitsetState 值,而是应该在下一个渲染周期中访问更新的状态,或者将值存储在一个变量中,使用它然后才调用 setState -
pilchard 编辑了,还是一样的输出
-
不能在同一个渲染周期中使用状态的更新值。
-
我会尝试一个自定义的 useState 钩子和回调来改变 setLikedNumbers 只有当图标改变时
标签: javascript reactjs state