React非严格模式下列表项右滑函数执行两次问题求助
React列表项右滑时handleRightSwipe重复执行两次的解决方案
问题分析
你遇到的onTouchEnd触发两次、handleRightSwipe重复执行的问题,核心原因是:
- NextUI的
Card组件开启isPressable后,会自带一套触摸/按压事件处理逻辑,和你自定义的触摸事件产生冲突,导致事件被重复触发。 - 触摸事件在DOM树中冒泡,Card内部的子元素(如
Row、Field)触发的touchEnd事件会传递到父级Card,造成多次执行。
解决方案
1. 阻止事件冒泡与默认行为
在触摸事件回调中添加stopPropagation和preventDefault,避免事件被多个元素处理:
onTouchStart={(e) => { e.stopPropagation(); e.preventDefault(); setTouchEnd(null); setTouchStart(e.targetTouches[0].clientX); }} onTouchMove={(e) => { e.stopPropagation(); e.preventDefault(); setTouchEnd(e.targetTouches[0].clientX); }} onTouchEnd={(e) => { e.stopPropagation(); e.preventDefault(); if (!touchStart || !touchEnd) return; const distance = touchStart - touchEnd; const isLeftSwipe = distance > minSwipeDistance; const isRightSwipe = distance < -minSwipeDistance; if(isLeftSwipe && finished) { handleLeftSwipe(); } if(isRightSwipe && !finished) { handleRightSwipe(); } // 处理完成后重置触摸状态,避免影响下一次滑动 setTouchStart(null); setTouchEnd(null); }}
2. 移除isPressable属性,自定义按压效果
isPressable自带的触摸逻辑会和你的滑动逻辑冲突,建议通过CSS模拟按压反馈替代:
- 移除Card的
isPressable={!finished}属性 - 添加自定义CSS:
.listItem:active { opacity: 0.8; transform: scale(0.98); transition: all 0.1s ease; }
3. 增加滑动防抖标记
添加状态标记当前是否正在处理滑动,彻底避免重复触发:
// 新增状态 const [isProcessingSwipe, setIsProcessingSwipe] = useState(false); // 修改onTouchEnd逻辑 onTouchEnd={(e) => { e.stopPropagation(); e.preventDefault(); if (!touchStart || !touchEnd || isProcessingSwipe) return; setIsProcessingSwipe(true); const distance = touchStart - touchEnd; const isLeftSwipe = distance > minSwipeDistance; const isRightSwipe = distance < -minSwipeDistance; if(isLeftSwipe && finished) { handleLeftSwipe(); } if(isRightSwipe && !finished) { handleRightSwipe(); } setTouchStart(null); setTouchEnd(null); // 延迟重置标记,确保滑动逻辑完成 setTimeout(() => setIsProcessingSwipe(false), 300); }}
修改后的Card组件核心代码
<Card onClick={() => { handleClick(false); }} onTouchStart={(e) => { e.stopPropagation(); e.preventDefault(); setTouchEnd(null); setTouchStart(e.targetTouches[0].clientX); }} onTouchMove={(e) => { e.stopPropagation(); e.preventDefault(); setTouchEnd(e.targetTouches[0].clientX); }} onTouchEnd={(e) => { e.stopPropagation(); e.preventDefault(); if (!touchStart || !touchEnd) return; const distance = touchStart - touchEnd; const isLeftSwipe = distance > minSwipeDistance; const isRightSwipe = distance < -minSwipeDistance; if(isLeftSwipe && finished) { handleLeftSwipe(); } if(isRightSwipe && !finished) { handleRightSwipe(); } setTouchStart(null); setTouchEnd(null); }} onContextMenu={() => handleClick(true)} css={{p: "0px 10px 0px 10px", w: 'auto', m: "6.5px", h: "55px", justifyContent: "center"}} className={ finished && swipedRight ? "listItem swipedRight finished" : finished && swipedLeft ? "listItem swipedLeft finished" : selected && swipedRight ? "listItem selected swipedRight" : selected ? "listItem selected" : swipedLeft ? "listItem swipedLeft" : swipedRight ? "listItem swipedRight" : finished ? "listItem finished" : "listItem" } > {/* 子组件内容保持不变 */} </Card>
内容的提问来源于stack exchange,提问作者Takkie253
相关产品推荐
相关产品推荐

