我正在尝试创建一个既具有常规点击又具有单独的操作的按钮,该按钮在用户单击并按住时会发生,类似于Chrome中的后退按钮。
我执行此操作的方式涉及一个setTimeout()
和一个回调,该回调检查状态是否正常。由于某种原因,回调从调用setTimeout()
开始就使用状态,而不是在调用回调时(1秒后)使用状态。
您可以在codesandbox
上查看它为了获得此功能,我在onMouseDown上致电setTimeOut()
。我还将状态中的isHolding设置为true。
onMouseUp我将isHolding设置为false,并且如果还没有时间调用hold函数,则运行clickHandler()
,这是一个道具。
setTimeOut()
中的回调将检查isHolding是否为true,如果为true,它将运行clickHoldHandler()
,这是一个道具。
isHolding处于状态(我正在使用钩子),但是当setTimeout()
触发它的回调时,我并没有返回当前状态,而是setTimetout()
首次被调用时的状态
这是我的做法:
const Button = ({ clickHandler, clickHoldHandler, children }) => {
const [isHolding, setIsHolding] = useState(false);
const [holdStartTime, setHoldStartTime] = useState(undefined);
const holdTime = 1000;
const clickHoldAction = e => {
console.log(`is holding: ${isHolding}`);
if (isHolding) {
clickHoldHandler(e);
}
};
const onMouseDown = e => {
setIsHolding(true);
setHoldStartTime(new Date().getTime());
setTimeout(() => {
clickHoldAction(e);
}, holdTime);
};
const onMouseUp = e => {
setIsHolding(false);
const totalHoldTime = new Date().getTime() - holdStartTime;
if (totalHoldTime < holdTime || !clickHoldHandler) {
clickHandler(e);
}
};
const cancelHold = () => {
setIsHolding(false);
};
return (
<button
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onMouseLeave={cancelHold}
>
{children}
</button>
);
};
答案 0 :(得分:2)
您应该将该回调任务包装到reducer中,并作为效果触发超时。是的,这肯定会使事情变得更加复杂(但这是“最佳实践”):
const Button = ({ clickHandler, clickHoldHandler, children }) => {
const holdTime = 1000;
const [holding, pointer] = useReducer((state, action) => {
if(action === "down")
return { holding: true, time: Date.now() };
if(action === "up") {
if(!state.holding)
return { holding: false };
if(state.time + holdTime > Date.now()) {
clickHandler();
} else {
clickHoldHandler();
}
return { holding: false };
}
if(action === "leave")
return { holding: false };
}, { holding: false, time: 0 });
useEffect(() => {
if(holding.holding) {
const timer = setTimeout(() => pointer("up"), holdTime - Date.now() + holding.time);
return () => clearTimeout(timer);
}
}, [holding]);
return (
<button
onMouseDown={() => pointer("down")}
onMouseUp={() => pointer("up")}
onMouseLeave={() => pointer("leave")}
>
{children}
</button>
);
};
工作沙箱:https://codesandbox.io/s/7yn9xmx15j
如果减速器变得过于复杂,可以作为备用,您可以记住一个设置对象(不是最佳实践):
const state = useMemo({
isHolding: false,
holdStartTime: undefined,
}, []);
// somewhere
state.isHolding = true;