useEffect仅在热重载时运行,子组件滑动事件绑定失效如何解决?
问题根因
- 子组件顶层直接取
const card = cardRef.current,组件首次渲染时DOM还未挂载,cardRef.current为null,后续即使DOM挂载完成,这个顶层变量也不会自动更新。只有热重载时组件二次渲染、DOM已存在的情况下才会拿到正确的DOM节点,所以事件绑定仅偶尔生效。 - 事件绑定
useEffect没有指定依赖数组,且没有做事件解绑逻辑,不仅会导致重复绑定,还会引发内存泄漏。 - 子组件中
setSwiped是异步更新,直接在setSwiped后调用props.parentCallback(swiped),传递的是更新前的旧值,会导致父组件计数异常。
修复方案
子组件修改后代码
function Profile(props: any) { const [name] = useState(`${props.profile.name.title} ${props.profile.name.first} ${props.profile.name.last}`); const [swiped, setSwiped] = useState(0) const backgroundImage = { backgroundImage: `url(${props.profile.picture.large})` }; const cardRef = useRef<HTMLDivElement>(null); let startX:any = null; function unify (e:any) { return e.changedTouches ? e.changedTouches[0] : e }; function lock (e:any) { // 直接从ref取最新的DOM节点,不要用顶层变量 if (cardRef.current) { startX = unify(e).clientX; console.log(startX) } } function move (e: any) { console.log('move') if(startX) { let differenceX = unify(e).clientX - startX, sign = Math.sign(differenceX); if(sign < 0 || sign > 0) { setSwiped((prev) => { const newSwiped = prev +1 // 拿到更新后的值再传给父组件 props.parentCallback(newSwiped); return newSwiped }) startX = null } } } // 依赖数组留空,仅在组件挂载完成后执行一次绑定,卸载时解绑 useEffect(() => { const card = cardRef.current; if (card) { console.log(card) card.addEventListener('mousedown', lock, false); card.addEventListener('touchstart', lock, false); card.addEventListener('mouseup', move, false); card.addEventListener('touchend', move, false); } // 返回清理函数,组件卸载时解绑事件 return () => { if (card) { card.removeEventListener('mousedown', lock, false); card.removeEventListener('touchstart', lock, false); card.removeEventListener('mouseup', move, false); card.removeEventListener('touchend', move, false); } } }, []) return ( <div> <h1 className="heading-1">{name}</h1> <div ref={cardRef} className="card" style={backgroundImage}> </div> </div> ); }
父组件说明
父组件现有逻辑可正常运行,无需额外修改。
内容的提问来源于stack exchange,提问作者codeDragon
相关产品推荐
相关产品推荐

