React实现类YouTube唯一投票点赞点踩按钮,解决点击后数值反复累加问题
现有实现的核心问题
- 组件未追踪用户的有效投票状态,点击事件触发时无前置校验,直接对计数做增减操作,导致连续点击时数值持续累加
- 未实现YouTube类的投票互斥逻辑,和“单用户单次有效投票”的需求不匹配
- 原实现未做已投票状态的UI提示,用户无法感知自己的投票状态
修复方案
核心逻辑是新增独立的投票状态追踪,所有计数操作都基于投票状态做分支判断,禁止无校验直接修改计数。
- 新增
userVote状态,可选值为like/dislike/null(未投票),记录当前用户的投票选择 - 对点赞、点踩的点击逻辑做全分支覆盖:
- 重复点击已投过的按钮:可选择实现取消投票(回退对应计数,状态重置为未投票),或直接拦截不执行操作
- 从一方切换到另一方投票:回退上一次的计数调整,再执行新的投票计数,更新投票状态
- 首次投票:直接执行对应计数调整,更新投票状态
- 根据投票状态给按钮加上高亮类,做已投票的视觉提示
修改后的完整组件代码
import React, { useState } from 'react'; const SingleMovieCard = ({singleMovie : {id, title, image ,category, likes, dislikes}, removeMovie}) => { // 初始化点赞、点踩的计数状态 const [isLike, setIsLike] = useState(likes); const [isDislike, setIsDislike] = useState(dislikes); // 新增:追踪用户投票状态,可选值 'like' / 'dislike' / null const [userVote, setUserVote] = useState(null); const likeMovie = () => { // 已点赞:取消投票 if (userVote === 'like') { setIsLike(prev => prev - 1); setUserVote(null); return; } // 之前点过踩:切换为点赞 if (userVote === 'dislike') { setIsDislike(prev => prev + 1); // 回退之前的点踩计数 setIsLike(prev => prev + 1); setUserVote('like'); return; } // 首次点赞 setIsLike(prev => prev + 1); setUserVote('like'); } const dislikeMovie = () => { // 已点踩:取消投票 if (userVote === 'dislike') { setIsDislike(prev => prev + 1); setUserVote(null); return; } // 之前点过赞:切换为点踩 if (userVote === 'like') { setIsLike(prev => prev - 1); // 回退之前的点赞计数 setIsDislike(prev => prev - 1); setUserVote('dislike'); return; } // 首次点踩 setIsDislike(prev => prev - 1); setUserVote('dislike'); } return ( <section key={id} className="single-movie"> <img src={image} alt={title}/> <footer> <div className="movie-info"> <h2 className="title">{title}</h2> <h4 className="category">{category}</h4> </div> <div className="ratio-section"> <div> <i onClick={likeMovie} className={`far fa-thumbs-up ${userVote === 'like' ? 'active' : ''}`} ></i> <h5>{isLike}</h5> </div> <div> <i onClick={dislikeMovie} className={`far fa-thumbs-down ${userVote === 'dislike' ? 'active' : ''}`} ></i> <h5>{isDislike}</h5> </div> </div> <button className="btn" onClick={()=> removeMovie(id)}>Delete </button> </footer> </section> ) } export default SingleMovieCard;
严格单次投票规则适配
如果需求要求用户投过票后无法修改、无法取消,只需要在likeMovie和dislikeMovie函数的最开头加入拦截逻辑:
// 已投票状态直接终止后续操作 if (userVote !== null) return;
加入该逻辑后,用户首次投票完成后,再次点击任何投票按钮都不会修改计数,完全符合“单用户一次有效投票”的要求。
内容的提问来源于stack exchange,提问作者David Jay
相关产品推荐
相关产品推荐

