React DnD-Kit拖拽结束后输入框获焦但无法输入的问题解决
拖拽结束后输入框获焦但无法输入的问题解决(Next.js + @dnd-kit/core)
问题情况
用Next.js、React、@dnd-kit/core和TypeScript开发时碰到个奇怪的问题:
- 直接调用
testRef?.current?.focus(),输入框能正常获焦并输入内容 - 但通过
useDndMonitor的onDragEnd事件触发focus后,输入框明明显示焦点头像,日志也打印了"focus",就是没法输入 - 用按钮点击触发focus却完全正常
复现代码:
"use client"; import { DragEndEvent, useDndMonitor, useDraggable, useDroppable, } from "@dnd-kit/core"; import React, { useRef } from "react"; function page() { const testRef = useRef(null); useDndMonitor({ onDragEnd: (event: DragEndEvent) => { if (testRef.current) { console.log("focus"); (testRef?.current as HTMLInputElement).focus(); } }, }); const draggable = useDraggable({ id: "drag", }); const droppable = useDroppable({ id: "drop", }); return ( <div> <div ref={draggable.setNodeRef} {...draggable.listeners} {...draggable.attributes} className="h-10 w-10 border border-yellow-200" > drag </div> <div ref={droppable.setNodeRef} className="h-10 w-10 border border-green-200" > drop </div> <form onSubmit={() => console.log("submit")}> <input ref={testRef} defaultValue="default " /> <input type="submit" /> </form> </div> ); } export default page;
解决方法
把onDragEnd里的focus调用放到setTimeout或者requestAnimationFrame里,延迟到下一个事件循环执行:
修改后的useDndMonitor代码:
useDndMonitor({ onDragEnd: (event: DragEndEvent) => { if (testRef.current) { console.log("focus"); // 用setTimeout延迟执行 setTimeout(() => { (testRef.current as HTMLInputElement).focus(); }, 0); // 或者用requestAnimationFrame,效果类似 // requestAnimationFrame(() => { // (testRef.current as HTMLInputElement).focus(); // }); } }, });
根本原因
@dnd-kit在拖拽结束的流程里,会同步执行一系列内部DOM清理和状态重置操作——比如移除拖拽时生成的临时元素、重置鼠标事件监听等。
你在onDragEnd里直接调用focus时,输入框虽然暂时拿到了焦点,但@dnd-kit后续的收尾操作会干扰焦点状态,或者导致浏览器的输入上下文没有正确绑定到输入框上,最终就出现了“有焦点头像但无法输入”的情况。
用setTimeout或requestAnimationFrame把focus操作延后,等@dnd-kit的所有拖拽收尾工作完成后再执行,就能避开这种干扰,让输入框正常接收键盘输入。
内容的提问来源于stack exchange,提问作者Lee Ronnie
相关产品推荐
相关产品推荐

