
本文介绍在使用 react-beautiful-dnd 时,如何根据视口宽度动态禁用拖拽行为——在移动设备(如窗口宽度本文介绍在使用 react-beautiful-dnd 时,如何根据视口宽度动态禁用拖拽行为——在移动设备(如窗口宽度
在实际项目中,
react-beautiful-dnd的isDragDisabled属性本身是响应式的,但其生效前提是组件能正确感知并响应尺寸变化。你提供的代码中存在两个关键问题:
useState(window.innerWidth 仅在组件挂载时读取一次初始宽度,未监听后续 resize;- 即使
useEffect正确注册了resize监听器,isMobileView状态更新后,Draggable组件内部的 drag logic 可能因 memoization 或内部缓存未能及时同步(尤其在列表项频繁重渲染场景下)。✅ 推荐方案:结合
useEffect+useRef实现响应式控制,并确保isDragDisabled始终为最新值以下是优化后的完整实现:
import { useState, useEffect, useRef } from 'react'; import { Draggable } from 'react-beautiful-dnd'; function ProductItem({ objectId, name, availability, images = [], idx, category, }) { const [isMobileView, setIsMobileView] = useState(false); const resizeTimerRef = useRef<nodejs.timeout null>(null); useEffect(() => { const updateMobileState = () => { setIsMobileView(window.innerWidth { if (resizeTimerRef.current) clearTimeout(resizeTimerRef.current); resizeTimerRef.current = setTimeout(updateMobileState, 150); }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); if (resizeTimerRef.current) clearTimeout(resizeTimerRef.current); }; }, []); return ( <draggable draggableid="{`${category}-${idx}`}" index="{idx}" isdragdisabled="{isMobileView}"> {(provided) => ( <div classname="item" ref="{provided.innerRef}"> @@##@@ <div> <h3 classname="title">{name}</h3> </div> <productavailability id="{objectId}" availability="{availability}"></productavailability> {provided.placeholder} </div> )} </draggable> ); } export default ProductItem;</nodejs.timeout>? 关键改进说明:
- 初始化即检测:
useEffect内首次调用updateMobileState(),确保服务端渲染(SSR)或快速加载时状态不为false默认值;- 防抖 resize:避免窗口拖拽缩放时频繁触发重渲染,提升性能;
- 严格清理定时器:防止内存泄漏与状态错乱;
isDragDisabled直接绑定 state:确保每次渲染都使用当前最新值,规避闭包 stale state 问题。⚠️ 注意事项:
- 不要将
window.innerWidth直接写入useState初始化参数(如useState(window.innerWidth ),因为 SSR 环境下 <code>window未定义会导致报错;- 若使用
React.memo包裹ProductItem,请确保isMobileView被正确纳入依赖或通过 context / custom hook 提供,否则 props 不变时组件不会重渲染;- 对于更复杂的响应式策略(如基于
matchMedia),可替换resize逻辑为window.matchMedia('(max-width: 767px)')监听,语义更强且原生支持媒体查询变更。? 进阶建议:
将设备检测逻辑抽离为自定义 Hook(如useIsMobile()),便于全项目复用,并支持服务端兼容判断(例如结合typeof window !== 'undefined'和 UA 检测兜底)。至此,你的拖拽组件即可在桌面端流畅拖动、移动端静默禁用,体验一致且健壮。
![]()











