When the first request is sent, isVoting will be set to "true". In this case, until isVoting is set to "false", another request should not be allowed to be sent. But somehow isVoting is still "false" when i try to click downvote multiple times fast. And downvote request is being sent two times, which is incorrect. How can I solve this?
"use client";
import { downvoteAnswer } from "@/lib/actions/answer.action";
import Image from "next/image";
import { usePathname, useRouter } from "next/navigation";
import { useState } 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) => {
const [isVoting, setIsVoting] = useState(false);
const pathname = usePathname();
const router = useRouter();
const handleVote = async (action: string) => {
console.log("isVoting:", isVoting);
if (isVoting) {
console.log("Request denied");
return;
}
setIsVoting(true);
await downvoteAnswer({
answerId: JSON.parse(itemId),
userId: JSON.parse(userId),
hasupVoted,
hasdownVoted,
path: pathname,
});
setIsVoting(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;
4
Answers
React states update are asynchronous, and there could be a delay in updating the isVoting state across renders.
Just like @hairyhandkerchief23 said you should use a useRef() with a boolean.
You could try it like this:
Hope this helps
Practically speaking, you should always disable a particular element if you want to avoid having multiple clicks on it.
The simplest answer that comes to mind is simply wrapping up the image inside a button and disabling it when needed. A little something like:
For this we can use useCallback and try catch
useCallback Hook: The handleVote function is wrapped in useCallback to ensure it doesn’t get recreated on every render, which could lead to unnecessary re-renders and potential bugs.
try-finally Block: The try-finally block is used to ensure that setIsVoting(false) is always called, even if the downvoteAnswer function throws an error. This ensures that the voting state is properly reset, allowing for future votes.
You define a ref not directly by a boolean value, but instead by an object: