React中拖拽功能仅对最后添加的Button生效,问题出在哪?
问题描述
我在React组件中实现拖拽功能时遇到了问题,希望能在应用中拖拽不同的按钮。为此我创建了如下Hook:
const [{isDragging}, drag] = useDrag(() => ({ type: 'button', collect: monitor => ({ isDragging: !!monitor.isDragging(), }), }))
之后在返回结构中,我将其作为ref使用:
{articleList.map((item, idx) => { return( <Accordion expanded={isElementExpanded(item.id)} onToggle={handleToggle2(item.id)} id={item.id} size="S"> ... {item.Blocks.map((block, index) => { return( ... <Button id={'button-'+index} ref={drag} ...></Button>...
问题在于只有最后添加的一个Button可拖拽,其他按钮的draggable属性均为"false"。奇怪的是我给所有Button都设置了ref={drag}。请问我的错误出在哪里?
问题原因与解决方案
核心问题
你只创建了一个useDrag实例,但把它的drag ref绑定到了多个按钮上。React的ref本质是一个引用,当你在循环中重复赋值同一个ref时,它最终只会指向最后一个被渲染的元素,这就导致只有最后一个按钮能获得拖拽能力。
解决方法
为每个按钮单独创建useDrag实例,最直接的方式是把可拖拽按钮封装成独立组件,在组件内部调用useDrag钩子:
// 封装可拖拽按钮组件 const DraggableButton = (props) => { const [{ isDragging }, drag] = useDrag(() => ({ type: 'button', collect: monitor => ({ isDragging: !!monitor.isDragging(), }), })); return ( <Button {...props} ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }} // 可选:添加拖拽时的视觉反馈 /> ); };
之后在循环中使用这个封装好的组件即可:
{articleList.map((item, idx) => { return( <Accordion expanded={isElementExpanded(item.id)} onToggle={handleToggle2(item.id)} id={item.id} size="S"> ... {item.Blocks.map((block, index) => { return( ... <DraggableButton id={'button-'+index} ...></DraggableButton>...
原理说明
每个DraggableButton组件都会独立调用useDrag,生成专属的drag ref和拖拽状态,不会出现多个元素共用同一个ref导致的覆盖问题,这样所有按钮都能拥有独立的拖拽能力。
内容的提问来源于stack exchange,提问作者Dr. Sven Alisch
相关产品推荐
相关产品推荐

