You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 14:06:03