如何避免React移动端Testimonial滑动至索引0时回滑崩溃
问题
我有一个供用户滚动浏览的testimonials组件,在移动端实现了滑动检测功能,可根据滑动方向切换数组索引,正向循环正常,但当索引为0时回滑会导致应用崩溃。如何确保索引回滑时循环到数组末尾?
问题代码片段
const [touchPosition, setTouchPosition] = useState(null) const handleTouchStart = (e) => { const touchDown = e.touches[0].clientX setTouchPosition(touchDown) } const handleTouchMove = (e) => { const touchDown = touchPosition if(touchDown === null) { return } const currentTouch = e.touches[0].clientX const diff = touchDown - currentTouch if (diff > 5) { setIdx((prevIndex) => prevIndex === testimonials.length - 1 ? 0 : prevIndex + 1 ) } if (diff < -5) { setIdx((prevIndex) => prevIndex === testimonials.length - 1 ? 0 : prevIndex - 1 ) } setTouchPosition(null) }
完整组件代码
const Testimonials = props =>{ const testimonials = [ { name: 'Name1', position: 'Title1', photo: require('./img/chrisphoto.png'), text: 'Body1' }, { name: 'Name2', position: 'Title2', photo: require('./img/jillphoto.png'), text: 'Body2' }, { name: 'Name3', position: 'Title3', photo: require('./img/mikephoto.png'), text: 'Body3' }, ]; const [idx, setIdx] = useState(0); let name = testimonials[idx].name; let position= testimonials[idx].position; let photo= testimonials[idx].photo; let text = testimonials[idx].text; const [touchPosition, setTouchPosition] = useState(null) const handleTouchStart = (e) => { const touchDown = e.touches[0].clientX setTouchPosition(touchDown) } const handleTouchMove = (e) => { const touchDown = touchPosition if(touchDown === null) { return } const currentTouch = e.touches[0].clientX const diff = touchDown - currentTouch if (diff > 5) { setIdx((prevIndex) => prevIndex === testimonials.length - 1 ? 0 : prevIndex + 1 ) } if (diff < -5) { setIdx((prevIndex) => prevIndex === testimonials.length - 1 ? 0 : prevIndex - 1 ) } setTouchPosition(null) } useEffect(() => { const interval = setInterval( () => setIdx(idx => (idx + 1) % testimonials.length), 120000, ); return () => { clearInterval(interval); }; }, []); return ( <div className="testimonials_main"> <div className="home-testimonial-container" onTouchStart={handleTouchStart} onTouchMove={handleTouchMove} > <img className="quotes" src={quotes}/> <img className="quotes2" src={quotes2}/> <div className='testimonial-entry'> <button className="next-testimonial" onClick={() => { setIdx(idx => (idx + 1) % testimonials.length); }}style={{ backgroundImage: `url(${nextarrow})`, backgroundRepeat: 'no-repeat', backgroundPosition: 'center', backgroundSize: '10px'}}></button> <button className="last-testimonial" onClick={() => { setIdx(idx => (idx - 1) % testimonials.length); }}style={{ backgroundImage: `url(${lastarrow})`, backgroundRepeat: 'no-repeat', backgroundPosition: 'center', backgroundSize: '10px'}}></button> <img className='testimonial-photo' src={photo} ></img> <div className='testimonial-text'> <h3 className='testimonial-name2' >{name}</h3></div> <div className='testimonial-text2'><h3 className='testimonial-title2' >{position}</h3></div> <div className='testimonial-body-container'><h3 className='testimonial-body2' style={{fontStyle:"italic"}}>{text}</h3> </div> </div> <div className="testimonialDots"> {testimonials.map((_, index) => ( <div key={index} className={`testimonialDot${index === idx ? " active" : ""}`} onClick={() => { setIdx(index); }}></div> ))} </div> </div> </div> ); } export default Testimonials;
解决方案
核心错误修复
崩溃的原因是回滑逻辑的判断条件完全写反了:左滑(diff>5)切换下一条的逻辑是对的,但右滑(diff<-5)切换上一条时,你错误地判断「如果是最后一条就回到0」,实际应该判断「如果是第一条(索引0)就跳到最后一条」,否则索引减1。
修正后的handleTouchMove代码:
const handleTouchMove = (e) => { const touchDown = touchPosition if(touchDown === null) { return } const currentTouch = e.touches[0].clientX const diff = touchDown - currentTouch const total = testimonials.length if (diff > 5) { // 左滑:下一条,到最后则回到0 setIdx((prevIndex) => prevIndex === total - 1 ? 0 : prevIndex + 1) } if (diff < -5) { // 右滑:上一条,到第一条则跳到最后 setIdx((prevIndex) => prevIndex === 0 ? total - 1 : prevIndex - 1) } setTouchPosition(null) }
额外修复:按钮点击的负模运算问题
你的「上一条」按钮用了(idx - 1) % testimonials.length,但JavaScript中负数取模会得到负数(比如0-1=-1,-1%3=-1),这同样会导致testimonials[-1]报错。需要改成:
<button className="last-testimonial" onClick={() => { setIdx(idx => (idx - 1 + testimonials.length) % testimonials.length); }}>...</button>
通过加上数组长度确保计算结果为非负数,再取模实现循环。
优化建议
可以统一用模运算简化循环逻辑,避免分支判断,代码更简洁不易出错:
- 下一条:
(prevIndex + 1) % total - 上一条:
(prevIndex - 1 + total) % total
优化后的handleTouchMove:
const handleTouchMove = (e) => { const touchDown = touchPosition if(touchDown === null) return const currentTouch = e.touches[0].clientX const diff = touchDown - currentTouch const total = testimonials.length if (diff > 5) { setIdx(prev => (prev + 1) % total) } else if (diff < -5) { setIdx(prev => (prev - 1 + total) % total) } setTouchPosition(null) }
内容的提问来源于stack exchange,提问作者Keith Lavoie
相关产品推荐
相关产品推荐

