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

Next.js中useState使用异常:快速点击重复发起踩赞请求问题

解决Next.js中踩赞按钮快速点击导致重复请求的问题

问题原因

useState的状态更新是异步批量处理的,当快速多次点击按钮时,第一次点击触发的setIsVoting(true)还未完成状态更新,第二次点击就已经进入handleVote函数,此时isVoting仍为初始的false,导致重复执行请求逻辑。

解决方案:使用useRef跟踪投票状态

useRef的值是同步更新且实时可读的,不会像useState那样存在异步延迟,能准确拦截重复的点击请求。

修改后的代码如下:

"use client";
import { downvoteAnswer } from "@/lib/actions/answer.action";
import Image from "next/image";
import { usePathname, useRouter } from "next/navigation";
import { useRef } from "react";

interface Props {
  type: string;
  itemId: string;
  userId: string;
  upvotes: number;
  hasupVoted: boolean;
  downvotes: number;
  hasdownVoted: boolean;
  authorId: string;
  hasSaved?: boolean;
}

const Votes = ({
  type,
  itemId,
  userId,
  upvotes,
  hasupVoted,
  authorId,
  downvotes,
  hasdownVoted,
  hasSaved,
}: Props) => {
  // 使用useRef替代useState跟踪投票状态
  const isVotingRef = useRef(false);
  const pathname = usePathname();
  const router = useRouter();

  const handleVote = async (action: string) => {
    // 直接读取ref的当前值,实时判断是否正在投票
    if (isVotingRef.current) {
      console.log("Request denied");
      return;
    }

    isVotingRef.current = true;
    try {
      await downvoteAnswer({
        answerId: JSON.parse(itemId),
        userId: JSON.parse(userId),
        hasupVoted,
        hasdownVoted,
        path: pathname,
      });
    } finally {
      // 无论请求成功或失败,都要重置状态
      isVotingRef.current = false;
    }
  };


  return (
    <div className="flex gap-5">
        <div className="flex-center gap-1.5">
          <Image
            src={
              hasdownVoted
                ? "/assets/icons/downvoted.svg"
                : "/assets/icons/downvote.svg"
            }
            width={18}
            height={18}
            alt="downvote"
            className="cursor-pointer"
            onClick={() => handleVote("downvote")}
          />
        </div>
    </div>
  );
};

export default Votes;

额外优化点

  • 增加try/finally块:确保即使请求失败(比如网络错误),isVotingRef.current也能被重置为false,避免按钮永久处于不可点击状态。
  • 无需依赖状态更新触发重渲染:这里仅用状态控制请求并发,不需要UI随isVoting变化,所以useRef完全满足需求。

内容的提问来源于stack exchange,提问作者Muhammadali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 05:09:57