React评论功能提交按钮单次点击触发两次回调问题求助
问题根因
你遇到的回调执行两次问题核心是重复绑定了点击事件监听器,具体原因如下:
- 你没有用React自带的
onClick属性绑定事件,而是用原生addEventListener手动给#confirm按钮绑定监听,且每次getComments执行时都会新增一个监听器,旧的监听器没有被清理,点一次按钮就会触发所有绑定过的回调。 - 你的
useEffect依赖项是prev,每次点击回复修改prev都会重新触发getComments,监听器就会多绑一次,你现在触发两次说明已经绑了2个监听器,后续如果继续操作还会触发更多次。 - 你写的
clicked状态拦截完全无效:每个监听器的回调都是独立闭包,拿到的clicked是绑定时的旧值,不是最新的提交状态,自然拦不住重复执行。
修复方案
优先方案(推荐)
完全去掉所有手动绑定事件的逻辑,改用React标准的事件绑定方式:
- 把提交评论的逻辑单独抽成一个公共异步函数,避免
ifNoCom和ifCom里重复写冗余代码 - 直接在你返回的
Confirm组件上添加onClick属性绑定这个抽离好的提交函数:
// 抽离提交逻辑 const submitComment = async () => { if(clicked) return cogoToast.warn("Already commenting...") setClicked(true) try { const content = document.querySelector("#commentcontent").value const {data} = await axios({ url: vars.BACKENDURL + "/comment", withCredentials: true, method: "POST", data: { article: article, comment: { content: content, prevId: prev === "" ? null : prev, }, }, }) if(typeof data != "object") { return cogoToast.error("Please sign in first!") } setPrev("") // 提交成功后重新拉取评论列表更新视图 getComments() } catch (err) { console.error(err) } finally { setClicked(false) } } // 组件里绑定事件 <Confirm id="confirm" onClick={submitComment}>Post</Confirm>
- 删掉
ifNoCom和ifCom里所有setTimeout加addEventListener的相关代码,完全不需要手动绑定。
如果你非要保留原生绑定的写法
必须在绑定新的监听器前移除旧的,或者在useEffect的清理函数里统一移除:
- 首先把回调函数定义成外部可引用的变量,不能写匿名函数,否则
removeEventListener找不到要移除的回调 - 每次绑定前先调用
removeEventListener移除上一次的回调 - 或者在useEffect的返回函数里统一清理所有监听器
其他可优化点
- 不要用
forEach加await遍历异步请求,forEach不支持异步等待,你写的await article.commentsArr.forEach完全无效,建议改成for...of循环或者Promise.all批量处理请求 - 你写的
!comments.includes({ ...data })判断永远为真,对象是引用类型,两个结构一样的对象也不会被includes判定为相等,这段判断没有任何作用 - 尽量不要用
document.querySelector拿DOM元素的value,评论输入框可以用受控组件,直接用useState存输入内容,更符合React的写法
内容的提问来源于stack exchange,提问作者ReginaTheMean
相关产品推荐
相关产品推荐

