React useState多次触发导致点赞按钮快速点击时数值异常问题
React点赞按钮快速点击数值异常问题
问题现象
我有一个点赞按钮,点击时通过setLikedNumbers(likedNumbers + 1);将默认值加1,再次点击则通过setLikedNumbers(likedNumbers - 1);减1。慢速点击运行正常,但每秒多次点击时会出现异常数值,已移除React strict mode标签。
视频开头我慢速点击按钮,随后快速每秒多次点击,之后axios处理完积压的请求后数值出现异常。
就算axios返回错误码前按钮被多次点击增减数值,增减次数相等的情况下不应该回到原始值吗?
疑似问题代码
组件代码
<ToggleIcon on={liked} onIcon={ <FavoriteOutlinedIcon onClick={() => unlike(props.idData)} /> } offIcon={ <FavoriteBorderIcon onClick={() => like(props.idData)} /> } />
逻辑代码
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
经过多次试验,基本确定问题是按钮切换动画过慢导致触发了错误的函数,比如本该触发unlike却触发了like,反之亦然。
更新2
通过创建包装函数,无论当前展示哪个按钮都能调用正确的like/unlike函数,问题已解决。
内容的提问来源于stack exchange,提问作者AtanasB
相关产品推荐
相关产品推荐

