Framer Motion:监听resize更新状态后拖拽约束失效
问题原因
- resize事件监听未正确清理:你在
useEffect中添加和移除的是两个独立的箭头函数实例(每次创建新的箭头函数),导致组件无法正确移除resize监听。随着窗口多次调整,会积累大量重复的监听回调,频繁触发setWindowsSize引发组件频繁重渲染,干扰了motion组件的拖拽状态管理,最终导致拖拽约束失效。 - 组件频繁重渲染破坏motion内部状态:窗口调整触发的状态更新会让整个组件(包括motion.div)重新渲染,motion内部维护的拖拽状态可能被意外重置,使得原本的约束规则不再生效。
解决步骤
步骤1:修复resize事件的监听与清理逻辑
将resize回调抽离为独立函数,确保添加和移除监听使用同一个函数引用:
const [windowSize, setWindowsSize] = useState(window.innerWidth); // 用useCallback缓存函数引用,避免每次渲染生成新函数 const handleResize = useCallback(() => { setWindowsSize(window.innerWidth); }, []); useEffect(() => { window.addEventListener("resize", handleResize); // 移除监听时使用同一个函数引用 return () => window.removeEventListener("resize", handleResize); }, [handleResize]);
步骤2:确保拖拽约束的稳定性
如果你的拖拽约束是固定值,用useMemo缓存约束对象,避免每次重渲染生成新对象导致motion重新计算约束:
const dragConstraints = useMemo(() => ({ left: 0, right: 0 }), []); <motion.div dragConstraints={dragConstraints} initial={{ x: 0 }} drag="x" ref={ref} dragElastic={0.2} className="flex gap-8" >
如果约束需要随窗口尺寸动态调整,直接将约束与windowSize关联即可:
<motion.div dragConstraints={{ left: 0, right: windowSize - (ref.current?.offsetWidth || 0) }} initial={{ x: 0 }} drag="x" ref={ref} dragElastic={0.2} className="flex gap-8" >
步骤3:减少不必要的组件重渲染(可选)
如果组件其他部分不需要依赖windowSize,可以将拖拽组件用React.memo包裹,避免因窗口尺寸更新触发无意义的重渲染:
const DraggableDiv = React.memo(({ windowSize }) => { const ref = useRef(null); const dragConstraints = useMemo(() => ({ left: 0, right: 0 }), []); return ( <motion.div dragConstraints={dragConstraints} initial={{ x: 0 }} drag="x" ref={ref} dragElastic={0.2} className="flex gap-8" > {/* 组件内容 */} </motion.div> ); });
内容的提问来源于stack exchange,提问作者Marcin Maciąg
相关产品推荐
相关产品推荐

