我的 React(非严格模式)函数在滑动手势中执行两次。

huangapple go评论55阅读模式
英文:

My React (not strict mode) function executes twice in swipe gesture

问题

我在React应用程序中的列表项组件上有某种滑动功能。滑动功能有效,但是右滑函数似乎执行了两次。我没有使用React StrictMode,所以这不是问题。

以下是我的列表项组件简化版:

<Card 
    isPressable={!finished} 
    onClick={() => {
        handleClick(false);
    }}
    onTouchStart={(e) => {
        setTouchEnd(null);
        setTouchStart(e.targetTouches[0].clientX);
    }}
    onTouchMove={(e) => {
        setTouchEnd(e.targetTouches[0].clientX);
    }}
    onTouchEnd={() => {
        if (!touchStart || !touchEnd) return;
        const distance = touchStart - touchEnd;
        const isLeftSwipe = distance > minSwipeDistance;
        const isRightSwipe = distance < -minSwipeDistance;
        if(isLeftSwipe && finished) {
            handleLeftSwipe();
        }
        else if(isRightSwipe && !finished) { 
            handleRightSwipe(); 
        }
    }} 
    onContextMenu={() => handleClick(true)}
/>

minSwipeDistance 是一个常量:const minSwipeDistance = 50;

当我向右滑动时,onTouchEnd 也会执行两次。handleSwipeRight 函数本身不需要调试,因为我仅将其替换为一个控制台日志,它仍然会执行两次。

在我的 useEffects 中,我没有做任何特殊处理。

在未完成的情况下,列表项不应该能向左滑动,反之亦然。

完整的列表项组件如下:

// 代码略...

希望这可以帮到您找到问题所在。如果您有任何其他疑问,请随时告诉我。

英文:

I have some kind of a swipe functionality on list items in my React application. The swipe functionality is working, but somehow the right swipe function gets executed twice. I am not using React StrictMode, so that isn't the problem.

Here is my list item component simplified:

&lt;Card 
        isPressable={!finished} 
        onClick={() =&gt; {
            handleClick(false);
        }}
        onTouchStart={(e) =&gt; {
            setTouchEnd(null);
            setTouchStart(e.targetTouches[0].clientX);
        }}
        onTouchMove={(e) =&gt; {
            setTouchEnd(e.targetTouches[0].clientX);
        }}
        onTouchEnd={() =&gt; {
            if (!touchStart || !touchEnd) return;
            const distance = touchStart - touchEnd;
            const isLeftSwipe = distance &gt; minSwipeDistance;
            const isRightSwipe = distance &lt; -minSwipeDistance;
            if(isLeftSwipe &amp;&amp; finished) {
                handleLeftSwipe();
            }
            else if(isRightSwipe &amp;&amp; !finished) { 
                handleRightSwipe(); 
            }
        }} 
        onContextMenu={() =&gt; handleClick(true)}

minSwipeDistance is a const: const minSwipeDistance = 50;

The onTouchEnd also executes twice when I swipe right. The handleSwipeRight function itself doesn't need to be debugged, because I literally exchanged it for only a console log and it was still being executed twice.

For the rest I am not doing anything special in my useEffects.

A listitem should not be able to be swiped left when the item is not finished and vice versa for finished items with right swipe.

我的 React(非严格模式)函数在滑动手势中执行两次。
我的 React(非严格模式)函数在滑动手势中执行两次。
我的 React(非严格模式)函数在滑动手势中执行两次。

My whole list Item component:
(swipedRight state is only for CSS purposes)

import { Avatar, Card, Modal, Row, Text } from &quot;@nextui-org/react&quot;;
import { useEffect, useState } from &quot;react&quot;;
import { useNavigate } from &quot;react-router-dom&quot;;
import { finishOrderRule, showToast, getOrderRule } from &quot;../../utils/api.js&quot;;
import { useAuth } from &quot;../../App.js&quot;;
import { FontAwesomeIcon } from &quot;@fortawesome/react-fontawesome&quot;;
import { faInfo } from &quot;@fortawesome/free-solid-svg-icons&quot;;
export default function OrderRule(props){
const [amountOfFields, setAmountOfFields] = useState(props.datafields?.length);
const [finished, setFinished] = useState(props.data?.Finished);
const [swipedLeft, setSwipedLeft] = useState(false);
const [swipedRight, setSwipedRight] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [touchStart, setTouchStart] = useState(null);
const [touchEnd, setTouchEnd] = useState(null);
const [selected, setSelected] = useState(props.selected === props.data?.Oid);
const order = props.order;
const isAllRules = props.isAllRules;
const rule = props.data;
const datafields = props.datafields;
const navigate = useNavigate();
const { token } = useAuth();
const minSwipeDistance = 50; 
const noteField = {
name: &quot;Note&quot;, 
type: &quot;Regels&quot;, 
label: &quot;Notitie&quot;
}
useEffect(() =&gt; {
setSelected(props.selected === rule?.Oid);
}, [props.selected]);
useEffect(() =&gt; {
setAmountOfFields(props.datafields.length);
}, [props.datafields]);
function getParts(fieldname, isOrderProperty){
const parts = fieldname.split(&quot;.&quot;);
if(parts.length === 1) return isOrderProperty ? order?.[fieldname] : rule?.[fieldname];
else if (parts.length === 2) return isOrderProperty ? order?.[parts[0]]?.[parts[1]] : rule?.[parts[0]]?.[parts[1]];
}
function handleClick(long){
if (long) navigate(`/orders/${order?.Oid}/${rule?.Oid}`, { state: rule, replace: false });
else props.setSelectedRule(rule?.Oid);
}
function handleIconClick(e){
e.stopPropagation();
setIsOpen(true);
}
function handleRightSwipe(){
console.log(&quot;right swipe&quot;);
setSwipedRight(true);
setFinished(true);
finishOrderRule(rule?.Oid, true, token)
.catch(error =&gt; {
setFinished(false);
if(error.statusCode === 401) {
showToast(&quot;Je moet opnieuw inloggen of je hebt geen rechten om deze actie uit te voeren.&quot;, error.statusCode);
navigate(&quot;/login&quot;);
}
else { 
showToast(error.message, error.statusCode)
console.log(error); 
}
})
.finally(() =&gt; {
getOrderRule(rule?.Oid, token)
.then((data) =&gt; {
props.modifyRule(data.value[0]);
})
.catch(error =&gt; {
setFinished(true);
if(error.statusCode === 401) {
showToast(&quot;Je moet opnieuw inloggen of je hebt geen rechten om deze actie uit te voeren.&quot;, error.statusCode);
navigate(&quot;/login&quot;);
}
else { 
showToast(error.message, error.statusCode)
console.log(error); 
}
})
.finally(() =&gt; {
setSwipedRight(false);
props.setSelectedRule(null);
});
});
}
function handleLeftSwipe(){
console.log(&quot;left swipe&quot;);
setSwipedLeft(true);
setFinished(false);
finishOrderRule(rule?.Oid, false, token)
.catch(error =&gt; {
setFinished(true);
if(error.statusCode === 401) {
showToast(&quot;Je moet opnieuw inloggen of je hebt geen rechten om deze actie uit te voeren.&quot;, error.statusCode);
navigate(&quot;/login&quot;);
}
else { 
showToast(error.message, error.statusCode)
console.log(error); 
}
})
.finally(() =&gt; {
setSwipedLeft(false);
getOrderRule(rule?.Oid, token)
.then((data) =&gt; {
props.modifyRule(data.value[0]);
})
.catch(error =&gt; {
setFinished(true);
if(error.statusCode === 401) {
showToast(&quot;Je moet opnieuw inloggen of je hebt geen rechten om deze actie uit te voeren.&quot;, error.statusCode);
navigate(&quot;/login&quot;);
}
else { 
showToast(error.message, error.statusCode)
console.log(error); 
}
})
.finally(() =&gt; {});
});
}
function isDate(fieldname){ return fieldname.toLowerCase().includes(&quot;date&quot;) }
const ModalComponent = () =&gt; {
return (
&lt;Modal css={{zIndex: 10, m: 10}} closeButton open={isOpen} onClose={() =&gt; { setIsOpen(false); }}&gt;
&lt;Modal.Header css={{p: 0}}&gt;&lt;Text color=&quot;primary&quot; size={26}&gt;{rule?.Product?.Name}&lt;/Text&gt;&lt;/Modal.Header&gt;
&lt;Modal.Body css={{p: 20, pt: 0}}&gt;
&lt;Text weight=&quot;medium&quot; size={15} css={{textAlign: &quot;center&quot;}}&gt;{rule?.Product?.Description}&lt;/Text&gt;     
&lt;Text weight=&quot;medium&quot; size={15} css={{textAlign: &quot;center&quot;}}&gt;{rule?.Note}&lt;/Text&gt;     
&lt;/Modal.Body&gt;   
&lt;/Modal&gt;
);
}
const NoteField = ({ width }) =&gt; {
return (
&lt;div style={{pointerEvents: &quot;none&quot;, width: width, display: &quot;flex&quot;, justifyContent: &quot;center&quot;, alignContent: &quot;center&quot;}}&gt;
{(rule?.Note !== null &amp;&amp; 
&lt;Avatar onClick={(e) =&gt; handleIconClick(e)} color={&quot;white&quot;} size=&quot;sm&quot; css={{display: &quot;flex&quot;, pointerEvents: &quot;auto&quot;, border: &quot;2px solid black&quot;, alignContent: &quot;center&quot;, justifyContent: &quot;center&quot;, justifyItems: &quot;center&quot;, alignItems: &quot;center&quot;}} bordered icon={&lt;FontAwesomeIcon icon={faInfo} /&gt;} /&gt;) || (
&lt;Avatar color={&quot;white&quot;} size=&quot;sm&quot; css={{border: &quot;2px solid black&quot;, opacity: &quot;10%&quot;}} bordered icon={&lt;FontAwesomeIcon icon={faInfo} /&gt;} /&gt;)}
&lt;/div&gt;
);
}
const Field = ({ field }) =&gt; {
const obj = field.type === &quot;Orders&quot; ? getParts(field.name, true) : getParts(field.name, false);
const wi = ((100 / amountOfFields).toString() + &quot;%&quot;).toString();
return field.name === &quot;Note&quot; ? &lt;NoteField width={wi} /&gt; : &lt;Text weight=&quot;medium&quot; size={15} css={{pointerEvents: &quot;none&quot;, width: wi, textAlign: &quot;center&quot;, lineHeight: &quot;100%&quot;}}&gt;{isDate(field.name) ? new Date(obj).toLocaleDateString().toString() : typeof(obj) === &quot;boolean&quot; ? (obj === true ? &quot;Ja&quot; : &quot;Nee&quot;) : obj === &quot;&quot; ? &quot;-&quot; : obj === null ? &quot;-&quot; : obj}&lt;/Text&gt;;
}
return (
&lt;Card 
isPressable={!finished} 
onClick={() =&gt; { handleClick(false); }}
onTouchStart={(e) =&gt; {
setTouchEnd(null);
setTouchStart(e.targetTouches[0].clientX);
}}
onTouchMove={(e) =&gt; {
setTouchEnd(e.targetTouches[0].clientX);
}}
onTouchEnd={() =&gt; {
if (!touchStart || !touchEnd) return;
const distance = touchStart - touchEnd;
const isLeftSwipe = distance &gt; minSwipeDistance;
const isRightSwipe = distance &lt; -minSwipeDistance;
if(isLeftSwipe &amp;&amp; finished) {
handleLeftSwipe();
}
if(isRightSwipe &amp;&amp; !finished) { 
handleRightSwipe();
}
}} 
onContextMenu={() =&gt; handleClick(true)}
css={{p: &quot;0px 10px 0px 10px&quot;, w: &#39;auto&#39;, m: &quot;6.5px&quot;, h: &quot;55px&quot;, justifyContent: &quot;center&quot;}}
className={
finished &amp;&amp; swipedRight ? &quot;listItem swipedRight finished&quot; : 
finished &amp;&amp; swipedLeft ? &quot;listItem swipedLeft finished&quot; : 
selected &amp;&amp; swipedRight ? &quot;listItem selected swipedRight&quot; : 
selected ? &quot;listItem selected&quot; : 
swipedLeft ? &quot;listItem swipedLeft&quot; : 
swipedRight ? &quot;listItem swipedRight&quot; : 
finished ? &quot;listItem finished&quot; : 
&quot;listItem&quot;
}
&gt;
&lt;ModalComponent key=&quot;modal&quot; /&gt;
{isAllRules &amp;&amp; &lt;div style={selected ? {position: &#39;absolute&#39;, left: 2, top: -4.5} : {position: &#39;absolute&#39;, left: 5, top: -2.5}}&gt;&lt;Text size={13} color=&quot;primary&quot;&gt;{`Order ${order?.Number}`}&lt;/Text&gt;&lt;/div&gt;}
&lt;Row justify=&quot;space-evenly&quot; css={{alignItems: &quot;center&quot;}}&gt;
{datafields.map((field) =&gt; (
&lt;Field key={field.name} field={field} /&gt;
))}
&lt;Field key=&quot;Notitie&quot; field={noteField} /&gt;
&lt;/Row&gt;
&lt;/Card&gt;
);
}

答案1

得分: 0

我从Next.UI切换到Mantine.dev的UI库,不知怎么地,问题就解决了。

我觉得这可能与Card组件的渲染有关。

英文:

I switched UI-libraries from Next.UI to Mantine.dev and somehow that fixed this issue.

I think it had something to do with the rendering of the Card component.

huangapple
  • 本文由 发表于 2023年6月28日 23:52:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/76574831.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定