PrefaceThe source code is only more than 100 lines in total. After reading this, you can roughly understand the implementation ideas of some mature react drag libraries, such as react-dnd, and then you can get started with these libraries very quickly. The general effect of using hooks is as follows: Our goal is to implement a useDrag and useDrop hook, which can easily make elements draggable, and in each life cycle of dragging, as shown below, you can customize the message delivery (by the way, introduce several events that will be triggered by dragging).
How to use + source code explanation class Hello extends React.Component<any, any> { constructor(props: any) { super(props) this.state = {} } render() { return ( <DragAndDrop> <DragElement /> <DropElement /> </DragAndDrop> ) } } ReactDOM.render(<Hello />, window.document.getElementById("root")) As mentioned above, the function of the DragAndDrop component is to pass messages to all components that use useDrag and useDrop, such as which DOM the currently dragged element is, or you can add other information if you want. Let's take a look at its implementation. const DragAndDropContext = React.createContext({ DragAndDropManager: {} }); const DragAndDrop = ({ children }) => ( <DragAndDropContext.Provider value={{ DragAndDropManager: new DragAndDropManager() }}> {children} </DragAndDropContext.Provider> ) You can see that the message passing is implemented using react's Context API. The focus is on this DragAndDropManager. Let's look at the implementation. export default class DragAndDropManager { constructor() { this.active = null this.subscriptions = [] this.id = -1 } setActive(activeProps) { this.active = activeProps this.subscriptions.forEach((subscription) => subscription.callback()) } subscribe(callback) { this.id += 1 this.subscriptions.push({ callback, id: this.id, }) return this.id } unsubscribe(id) { this.subscriptions = this.subscriptions.filter((sub) => sub.id !== id) } } The function of setActive is to record which element is currently being dragged. It will be used in useDrag. When we look at the implementation of useDrag's hooks, we will understand that as long as we call the setActive method and pass the dragged DOM element into it, we will know which element is currently being dragged. In addition, I also added an API for subscribing to events, subscribe. I haven’t used it yet, so you can ignore it in this example. Just know that you can add subscription events. Next, let's take a look at the use of useDrag. The implementation of DragElement is as follows: function DragElement() { const input = useRef(null) const hanleDrag = useDrag({ ref: input, collection: {}, // You can fill in any message you want to pass to the drop element here, which will be passed to the drop element as a parameter later}) return ( <div ref={input}> <h1 role="button" onClick={handleDrag}> drag element </div> ) } Let's take a look at the implementation of useDrag, which is very simple export default function useDrag(props) { const { DragAndDropManager } = useContext(DragAndDropContext) const handleDragStart = (e) => { DragAndDropManager.setActive(props.collection) if (e.dataTransfer !== undefined) { e.dataTransfer.effectAllowed = "move" e.dataTransfer.dropEffect = "move" e.dataTransfer.setData("text/plain", "drag") // firefox fix } if (props.onDragStart) { props.onDragStart(DragAndDropManager.active) } } useEffect(() => { if (!props.ref) return () => {} const { ref: { current }, } = props if (current) { current.setAttribute("draggable", true) current.addEventListener("dragstart", handleDragStart) } return () => { current.removeEventListener("dragstart", handleDragStart) } }, [props.ref.current]) return handleDragStart } What useDrag does is very simple.
Among them, the use of useDrop and the implementation of DropElement are as follows: function DropElement(props: any): any { const input = useRef(null) useDrop({ ref: input, // e represents the event object of the element being dragged over when the dragOver event occurs // collection is the data stored in the store // showAfter indicates whether the mouse passes over the dropped element when the mouse is dragging the element (above is the upper half, below is the lower half) onDragOver: (e, collection, showAfter) => { // If it passes the upper half, the upper border of the drop element is red if (!showAfter) { input.current.style = "border-bottom: none;border-top: 1px solid red" } else { // If it passes the lower half, the top border of the drop element is red input.current.style = "border-top: none; border-bottom: 1px solid red" } }, // If you release the mouse on the drop element, the style is cleared onDrop: () => { input.current.style = "" }, // If you leave the drop element, the style is cleared onDragLeave: () => { input.current.style = "" }, }) return ( <div> <h1 ref={input}>drop element</h1> </div> ) } Finally, let's look at the implementation of useDrop export default function useDrop(props) { // Get the data in the outermost store const { DragAndDropManager } = useContext(DragAndDropContext) const handleDragOver = (e) => { // e is the drag event object e.preventDefault() // See the diagram of getBoundingClientRect below const overElementHeight = e.currentTarget.getBoundingClientRect().height / 2 const overElementTopOffset = e.currentTarget.getBoundingClientRect().top // clientY is the distance from the mouse to the top of the visible area of the browser page const mousePositionY = e.clientY // mousePositionY - overElementTopOffset is the distance from the mouse inside the element to the element border-top const showAfter = mousePositionY - overElementTopOffset > overElementHeight if (props.onDragOver) { props.onDragOver(e, DragAndDropManager.active, showAfter) } } // drop event const handledDop = (e: React.DragEvent) => { e.preventDefault() if (props.onDrop) { props.onDrop(DragAndDropManager.active) } } // dragLeave event const handledragLeave = (e: React.DragEvent) => { e.preventDefault() if (props.onDragLeave) { props.onDragLeave(DragAndDropManager.active) } } // Register events. Note that you should unregister events when destroying components to avoid memory leaks. useEffect(() => { if (!props.ref) return () => {} const { ref: { current }, } = props if (current) { current.addEventListener("dragover", handleDragOver) current.addEventListener("drop", handledDop) current.addEventListener("dragleave", handledragLeave) } return () => { current.removeEventListener("dragover", handleDragOver) current.removeEventListener("drop", handledDop) current.removeEventListener("dragleave", handledragLeave) } }, [props.ref.current]) } GetBoundingClientRect API diagram: rectObject = object.getBoundingClientRect(); rectObject.top: the distance from the top of the element to the top of the window; rectObject.right: the distance from the right side of the element to the left side of the window; rectObject.bottom: the distance from the bottom of the element to the top of the window; rectObject.left: the distance from the left side of the element to the left side of the window; This concludes this article about implementing react drag-and-drop hooks with more than 100 lines of code. For more relevant react drag-and-drop hooks content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future! You may also be interested in:
|
<<: Linux kernel device driver kernel debugging technical notes collation
>>: How to modify the initial password of a user in mysql5.7
Table of contents How to flatten an array 1. Usin...
Table of contents 1. Basic knowledge of indexing ...
In a page, there are many controls (elements or ta...
There are many read-write separation architecture...
Table of contents 1. Constructor and instantiatio...
1. Overview Zabbix is a very powerful and most ...
Table of contents Preface 1. Environment Configur...
A few days ago, I found that the official version...
In js, set the user to read a certain agreement b...
This article describes how to install the PHP cur...
This article uses examples to describe MySQL pess...
How to implement the "Set as homepage" ...
This article example shares the specific code for...
1. Use ansible's playbook to automatically in...
Table of contents What is virtual dom? Why do we ...