1 the introduction
Drag and drop is a very common interaction on the front end, but obviously drag and drop is a strong DOM interaction. React bypasses the DOM layer, so the React drag-and-drop solution is definitely worth talking about.
Let’s talk about React dragging with How To Use The HTML drag-and-drop API In React.
2 an overview
The original text is relatively simple, the author quickly introduces the key part.
First, there are four main drag apis: dragEnter dragLeave dragOver drop, which correspond to dragging in, dragging out, dragging within the current element scope, and completing the dragging action respectively.
With these apis, we can use React to implement a drag region:
import React from "react";
const DragAndDrop = props= > {
const handleDragEnter = e= > {
e.preventDefault();
e.stopPropagation();
};
const handleDragLeave = e= > {
e.preventDefault();
e.stopPropagation();
};
const handleDragOver = e= > {
e.preventDefault();
e.stopPropagation();
};
const handleDrop = e= > {
e.preventDefault();
e.stopPropagation();
};
return (
<div
className={"drag-drop-zone"}
onDrop={e= > handleDrop(e)}
onDragOver={e => handleDragOver(e)}
onDragEnter={e => handleDragEnter(e)}
onDragLeave={e => handleDragLeave(e)}
>
<p>Drag files here to upload</p>
</div>
);
};
export default DragAndDrop;
Copy the code
PreventDefault is to prevent the default response, it could be something like jump pages, stopPropagation is to prevent bubbling, so parent elements that also listen to events don’t get the response, we can be very specific to the nested children.
UseReducer: useReducer: useReducer: useReducer
. const reducer =(state, action) = > {
switch (action.type) {
case 'SET_DROP_DEPTH':
return { ...state, dropDepth: action.dropDepth }
case 'SET_IN_DROP_ZONE':
return { ...state, inDropZone: action.inDropZone };
case 'ADD_FILE_TO_LIST':
return { ...state, fileList: state.fileList.concat(action.files) };
default:
returnstate; }};const [data, dispatch] = React.useReducer(
reducer, { dropDepth: 0.inDropZone: false.fileList: []})...Copy the code
The last key point is the post-drag-in processing. Use Dispatch to add drag-in files and set the drag-in state:
const handleDrop = e= >{... let files = [...e.dataTransfer.files];if (files && files.length > 0) {
const existingFiles = data.fileList.map(f= > f.name)
files = files.filter(f= >! existingFiles.includes(f.name)) dispatch({type: 'ADD_FILE_TO_LIST', files });
e.dataTransfer.clearData();
dispatch({ type: 'SET_DROP_DEPTH'.dropDepth: 0 });
dispatch({ type: 'SET_IN_DROP_ZONE'.inDropZone: false}); }};Copy the code
The e.datatransfer. clearData function is used to clear temporary variables generated during the drag process. These temporary variables can be assigned by e.datatransfer. XXX = and are generally used for value transfer during the drag process.
To summarize, use HTML5’s API to convert drag and drop into state, and eventually state mapping to the UI.
The content of the original text is relatively simple, the author in the intensive reading part to expand some more systematic content.
3 intensive reading
At present, drag is mainly divided into two kinds, one is the drag of HTML5 native specification, this way does not affect the DOM structure in the drag process. The other is full WYSIWYG drag-and-drop. The DOM position changes during drag-and-drop. The advantage is that you can give immediate feedback on the drag-and-drop results, but the disadvantage is that it is flashy.
Because this article also uses the first kind of drag and drop scheme, because the author reorganizes again own package idea.
Working backwards from a usage perspective, assuming we have a drag-and-drop library, we must have two apis:
import { DragContainer, DropContainer } from 'dnd'
constDragItem = ( <DragContainer> {({ dragProps }) => ( <div {... dragProps} /> )} </DragContainer> ) const DropItem = ( <DropContainer> {({ dropProps }) => ( <div {... dropProps} /> )} </DropContainer> )Copy the code
DragContainer wraps around elements that can be dragged, and DragContainer wraps around elements that can be dragged in. As for dragProps and dropProps, they need to pass through the DOM node of the child element in order to control the drag effect using the DOM API. This is the only DOM requirement for drag and drop. Both elements need to be hosted by the physical DOM.
In the example above, the way of giving dragProps and dropProps is RenderProps. We can execute children as a function to achieve the effect:
const DragContainer = ({ children, componentId }) = > {
const { dragProps } = useDnd(componentId)
return children({
dragProps
})
}
const DropContainer = ({ children, componentId }) = > {
const { dropProps } = useDnd(componentId)
return children({
dropProps
})
}
Copy the code
A custom Hook useDnd is used to accept dragProps and dropProps.
const useDnd = ({ componentId }) = > {
const dragProps = {}
const dropProps = {}
return { dragProps, dropProps }
}
Copy the code
Next, we will implement drag and drop, respectively.
For Drag, simply implement onDragStart and onDragEnd:
const dragProps = {
onDragStart: ev= > {
ev.stopPropagation()
ev.dataTransfer.setData('componentId', componentId)
},
onDragEnd: ev= > {
// Do some drag-and-drop cleanup}}Copy the code
SetData is used to inform the drag side of the id of the component being dragged. This is because drag is initiated by drag and responded by drop. Therefore, there must be a data transfer process. And dataTransfer is perfect for that.
For drop, just implement onDragOver and onDrop:
const dropProps = {
onDropOver: ev= > {
// Do some styling to remind the user where releasing the element will prevent
},
onDrop: ev= > {
ev.stopPropagation()
const componentId = ev.dataTransfer.getData('componentId')
// Modify data with componentId and refresh UI with React Rerender}}Copy the code
The focus is onDrop, which is the “real execution” of the drag-and-drop effect and ultimately updates the data by modifying the UI.
There is a scenario where a container can be dragged or dragged in. In this case, the component is usually a container, but the container can be dragged into other containers and nested freely.
The way to implement this scenario is to apply DragContainer and DropContainer to one component:
const Box = (
<DragContainer>
{({ dragProps }) => (
<DropContainer>
{({ dropProps }) => {
<div {. dragProps} {. dropProps} / >
}}
</DropContainer>
)}
</DragContainer>
)
Copy the code
Nesting is possible because the HTML5 API allows an element to have both onDragStart and onDrop properties, whereas the syntax above simply passes both properties to the component DOM.
Implementing a drag library is as simple as using HTML5’s drag API and React’s special syntax.
4 summarizes
Finally, there is a question that many systems with drag function have the function of “drag placeholder”, that is, during the process of dragging elements, a horizontal or vertical line will be displayed at the “drop point” position, leading to the drop point of elements after release, as shown in the figure:
So how does this auxiliary line work? Feel free to leave a comment in the comments section! If you have auxiliary line implementation solution analysis of the article, welcome to share, you can also look forward to the future of the author to write a special “drag placeholder” implementation analysis of intensive reading.
The discussion address is: Intensive reading Handwritten JSON Parser · Issue #233 · dT-fe /weekly
If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.
Pay attention to the front end of intensive reading wechat public account
Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)