You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React非严格模式下列表项右滑函数执行两次问题求助

React列表项右滑时handleRightSwipe重复执行两次的解决方案

问题分析

你遇到的onTouchEnd触发两次、handleRightSwipe重复执行的问题,核心原因是:

  1. NextUI的Card组件开启isPressable后,会自带一套触摸/按压事件处理逻辑,和你自定义的触摸事件产生冲突,导致事件被重复触发。
  2. 触摸事件在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:17:01