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
相关产品推荐
相关产品推荐

