React拖拽场景state值异常:dropHandler中startBlock为何为空?
React拖拽场景下startBlock状态在dropHandler中为空的问题解答
问题原因
你遇到的是React闭包陷阱:
dropHandler函数在组件首次渲染时创建,它捕获了当时的startBlock初始值(空字符串)。- 虽然
dragStartHandler触发后startBlock状态更新、组件重新渲染并生成了新的dropHandler,但此时拖拽操作已经启动,绑定到元素上的仍是旧的dropHandler函数,执行时自然拿到初始的空字符串。 - 你看到
useEffect和组件内的console.log显示状态更新,是组件重新渲染后新周期的日志,和旧dropHandler所在的闭包无关。
修复方案
提供两种实用解决方法:
方法一:用useRef存储拖拽起始ID
useRef的.current属性是可变值,不受闭包限制,能始终获取最新数据:
export function SomePage() { const [startBlock, setStartBlock] = useState(''); const startBlockRef = useRef(''); useEffect(() => { startBlockRef.current = startBlock; console.log("changed to", startBlock); }, [startBlock]) function dragStartHandler(e) { const blockId = e.currentTarget.parentElement.id; setStartBlock(blockId); startBlockRef.current = blockId; } function dropHandler(e) { e.preventDefault(); console.log('drop', startBlockRef.current); } return ( <div draggable onDragStart={dragStartHandler} onDrop={dropHandler}> {/* 组件内容 */} </div> ) }
方法二:利用拖拽API的dataTransfer传递数据
这是HTML5拖拽的标准用法,无需依赖React状态:
export function SomePage() { function dragStartHandler(e) { const blockId = e.currentTarget.parentElement.id; e.dataTransfer.setData('text/plain', blockId); } function dropHandler(e) { e.preventDefault(); const startBlock = e.dataTransfer.getData('text/plain'); console.log('drop', startBlock); } return ( <div draggable onDragStart={dragStartHandler} onDrop={dropHandler}> {/* 组件内容 */} </div> ) }
注:原代码事件属性名有误,React中拖拽事件应为
onDragStart和onDrop,已在修复代码中修正。
内容的提问来源于stack exchange,提问作者George D.
相关产品推荐
相关产品推荐

