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

React中onTouch与onMouse事件同时触发致函数重复执行问题

问题原因

问题出在第三方组件的实现逻辑上:它将你传入的onClick回调同时绑定到了onMouseDown和onTouchStart两个事件上。在支持触摸的设备中,浏览器会在触发touchstart事件后自动模拟mousedown事件,导致两个事件先后触发,最终让你的handlePrevious执行两次。

你之前尝试的preventDefault()和stopPropagation()无效,是因为这些方法需要在事件触发的第一时间调用——而你是在最终的handlePrevious里调用,此时touchstart已经执行完毕,浏览器已经在准备触发mousedown了,自然无法阻止重复执行。

解决方案

根据你是否能修改第三方组件,提供两种处理思路:

1. 若能修改/二次封装第三方组件

方案一:改用原生onClick事件

直接去掉onMouseDown和onTouchStart的绑定,恢复原生onClick——它在触摸和鼠标设备上都能正常触发,且不会有重复执行问题:

type FooterIconButtonType = typeof FooterIconButtonInt;

export const FooterIconButton: FooterIconButtonType = (props: ComponentProps<FooterIconButtonType>) => {
  return (
    <FooterIconButtonInt {...props} onClick={props.onClick} onMouseDown={undefined} onTouchStart={undefined} />
  );
};

方案二:添加重复触发拦截

在组件内部用标志位控制,确保短时间内只执行一次回调:

import { useRef } from 'react';

type FooterIconButtonType = typeof FooterIconButtonInt;

export const FooterIconButton: FooterIconButtonType = (props: ComponentProps<FooterIconButtonType>) => {
  const isTriggered = useRef(false);

  const handleEvent = (e: React.MouseEvent | React.TouchEvent) => {
    if (isTriggered.current) return;
    isTriggered.current = true;
    props.onClick?.(e);
    // 300ms后重置标志,避免后续点击失效,可根据需求调整时长
    setTimeout(() => {
      isTriggered.current = false;
    }, 300);
  };

  return (
    <FooterIconButtonInt {...props} onClick={undefined} onMouseDown={handleEvent} onTouchStart={handleEvent} />
  );
};

2. 若无法修改第三方组件(仅在主应用处理)

方案一:用标志位拦截重复执行

在handlePrevious内部通过useRef记录是否已执行,避免短时间内重复触发:

import { useCallback, useRef } from 'react';

// ...
const handlePrevious = useCallback(() => {
  const hasExecuted = useRef(false);
  return () => {
    if (hasExecuted.current) return;
    hasExecuted.current = true;
    // do some stuff here!!
    setTimeout(() => {
      hasExecuted.current = false;
    }, 300);
  };
}, [])();

方案二:拆分事件并阻止默认行为

覆盖组件的事件绑定,分别处理touchstart和mousedown,在touchstart中调用preventDefault()阻止浏览器触发mousedown:

const handleTouchStart = (e: React.TouchEvent) => {
  e.preventDefault(); // 阻止浏览器模拟mousedown
  handlePrevious();
};

const handleMouseDown = () => {
  handlePrevious();
};

// 使用组件时
<FooterButtonsWrapper center style={{zIndex: 2, position: 'relative'}}>
  <FooterIconButton 
    hide={isGrid} 
    src={AppIcons.backSmall} 
    onClick={undefined} // 覆盖原组件的onClick绑定
    onMouseDown={handleMouseDown} 
    onTouchStart={handleTouchStart} 
  />
</FooterButtonsWrapper>

内容的提问来源于stack exchange,提问作者user25441075

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:05:02