React中如何给可点击图标绑定含setTimeout和clearTimeout的OnClick事件
核心问题
clearTimeout用法错误:该API仅接收定时器ID作为唯一入参,不存在第二个时间参数,且你在创建定时器后立即执行清除操作,定时器逻辑永远不会触发- 逻辑顺序颠倒:你需要的是点击瞬间触发背景高亮,再延迟恢复默认样式,现有代码是延迟设置高亮,和需求相反
- 定时器ID未持久化:函数组件每次渲染都会重建内部局部变量,存在临时变量里的定时器ID会丢失,无法跨渲染周期执行清除操作
修复代码
//rafce import React, { useState, useRef, useEffect } from 'react'; // styled components import styled from 'styled-components'; // icons import { IoIosArrowUp, IoIosArrowDown } from 'react-icons/io'; const DurationIntervalComponent = () => { const [hours, setHours] = useState('0'); const [showHoursArrows, setShowHourArrows] = useState(false); const [arrowActiveUp, setArrowActiveUp] = useState(false); const [arrowActiveDown, setArrowActiveDown] = useState(false); // 用ref持久化存储定时器ID const timerRef = useRef(null); // 组件卸载时清除残留定时器,避免内存泄漏 useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); const incrementHours = () => { // 先清除之前未执行的定时器,避免多次点击状态冲突 if (timerRef.current) clearTimeout(timerRef.current); // 点击瞬间立刻设置激活态 setArrowActiveUp(true); // 200ms后恢复默认样式,时长可自行调整 timerRef.current = setTimeout(() => { setArrowActiveUp(false); }, 200); setHours((prevHours) => { if (!prevHours) return '0'; if (+prevHours >= 24) return '0'; return String(+prevHours + 1); }); }; const decrementHours = () => { if (timerRef.current) clearTimeout(timerRef.current); setArrowActiveDown(true); timerRef.current = setTimeout(() => { setArrowActiveDown(false); }, 200); setHours((prevHours) => { if (!prevHours) return '0'; if (+prevHours <= 0) return '24'; return String(+prevHours - 1); }); }; return ( <Container> <Row> <p> Interval* </p> <Inputs> <Selection onMouseEnter={() => setShowHourArrows(true)} onMouseLeave={() => setShowHourArrows(false)} > {showHoursArrows && ( <p className="icon" onClick={incrementHours} style={ arrowActiveUp ? { backgroundColor: 'red' } : { backgroundColor: 'none' } } > <IoIosArrowUp /> </p> )} <SquareInput value={hours} /> <p>hours</p> {showHoursArrows && ( <p className="icon" onClick={decrementHours} style={ arrowActiveDown ? { backgroundColor: 'red' } : { backgroundColor: 'none' } } > <IoIosArrowDown /> </p> )} </Selection> <div> <SquareInput /> <p>minutes</p> </div> </Inputs> </Row> <hr /> <Row> <p> Duration* </p> <Inputs> <SquareInput /> <p>:</p> <SquareInput /> <p>to</p> <SquareInput /> <p>:</p> <SquareInput /> </Inputs> </Row> </Container> ); }; const Container = styled.div` border: 1px solid #c5c5c5; border-radius: 15px; `; const Row = styled.div` display: flex; align-items: center; height: 150px; justify-content: space-between; padding: 20px; `; const Inputs = styled.div` display: flex; align-items: center; p { margin-right: 5px; font-size: 0.8em; text-align: center; } `; const Selection = styled.div` .icon { font-size: 1.2em; padding: 0; cursor: pointer; } `; const SquareInput = styled.input` width: 50px; height: 50px; border: 1px solid #c5c5c5; border-radius: 10px; outline: none; margin-right: 5px; font-size: 1.5em; text-align: center; `; export default DurationIntervalComponent;
补充说明
可以根据实际需求调整定时器的延迟时长,已额外给箭头添加了cursor: pointer样式优化交互体验。
内容的提问来源于stack exchange,提问作者user14749773
相关产品推荐
相关产品推荐

