Creating drag-and-drop elements with React By Zoranin Tutorials - February 18, 2022 Comments: 0 Views: 575 In this tutorial, we'll look at creating drag-and-drop elements in React using Atlassian library . The article is intended for people familiar with React.You will learn how to create drag-and-drop elements in React and be able to create a game like this:Basic ConceptsDragDropContext: The place (field) where the drag-and-drop is actually performed. This component is called ondragEndafter the dragged object has been released. It can also be defined ondragStartto ondragUpdatebe called after the start of the drag and on some event during the drag, respectively.Droppable : The component where the element is being dropped from and to. This component needs several properties, which will be described next.Draggable : The element that will be dragged. Like droppable , it needs some properties to make the component moveable.Creating a gameLet's define the initial settings that are needed to create the game:const initialData = { column: { id: 'column-1', numberIds: ['four', 'one', 'five', 'three', 'two'], }, numbers: { 'five': { id: 'five', content: '5' }, 'four': { id: 'four', content: '4' }, 'one': { id: 'one', content: '1' }, 'three': { id: 'three', content: '3' }, 'two': { id: 'two', content: '2' }, } }; export default initialData;Now we can move on to creating the first component. It will contain only one method render. Actually, here it is:class NumbersGame extends React.Component<any, INumbersGameState>{ public constructor(props: any) { super(props); this.onDragEnd = this.onDragEnd.bind(this); this.state = {...initialData}; } public onDragEnd(result: any) { // Элемент отпущен! } public render() { const numbers = this.state.column.numberIds.map((numberId: string) => this.state.numbers[numberId]); return ( <NumbersGameContext onDragEnd={this.onDragEnd}> <VerticalColumn column={this.state.column} items={numbers} /> </NumbersGameContext> ) } }NumbersGameContext: it's just a wrapper for DragDropContext.VerticalColumn: This is the column containing the draggable items.export default (props: IVerticalColumnProps) => <DroppableWrapper droppableId={props.column.id} className="source"> <DraggableListItems items={props.items} /> </DroppableWrapper>DroppableDroppableWrapperis a component that implements a droppable container. It has the necessary properties, which are defined like this:export default (props: any) => <Droppable droppableId={props.droppableId}> {(provided: any) => ( <div className={props.className} ref={provided.innerRef} {...provided.droppableProps} {...provided.droppablePlaceholder}> {props.children} </div> )} </Droppable>It needs a droppableId, which must be unique within the DragDropContext. It also expects a function as a child element and uses the render props pattern to avoid external DOM tampering.The first argument to this function is provided. This argument has droppablePropsand is important for defining the bean as a droppable . Here you can apply some or all of the possible properties using the spread syntax. For more details, see the documentation .The second property is innerRef, a function that passes the required DOM element to the library.The last property is placeholder. This element is optionally used to increase the space in the droppable area while dragging. Placeholdershould be made a child of the droppable component. At this stage, the configuration of the component is completed.DraggableNow we can move on to writing DraggableListItems. This component creates NumberBox(dragged objects).export default (props: IDraggableListItems) => <div> {props.items.map(toNumberBox)} </div> function toNumberBox(item: INumberItemProps, position: number) { return <NumberBox key={item.id} className="box" itemPosition={position} value={item.id} content={item.content} /> }NumberBoxdefines a wrapper for draggable :export default (props: IDraggableItem) => { const className = `dnd-number size-${props.value}`; return ( <DraggableItemWrapper draggableId={props.value} index={props.itemPosition} className={className}> <div>{props.content}</div> </DraggableItemWrapper> ) }DraggableItemWrapperimplements draggable because, like droppable , it has the right properties.export default (props: any) => <Draggable draggableId={props.draggableId} index={props.index}> {(provided: any) => ( <div className={props.className} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}> {props.children} </div> )} </Draggable>This code implements the render props pattern already mentioned above . In this case, the draggable has two properties: draggableIdand index. DraggableIdmust be unique within DragDropContext. It also accepts a function as a child element. The first argument to the function is provided(as in droppable ). The second argument is dragHandleProps. This property defines the component as draggable .Now you can use DraggableItemWrapperand forget about low-level properties.ondragEndHere is what has already been implemented:- NumberGameContext: realization DragDropContext.- DroppableWrapper: droppable implementation.- DraggableItemWrapper: draggable implementation.And here are the components that are part of the game:- VerticalColumn: A component that encapsulates droppable columns and draggable elements.- DraggableListItems: A component that encapsulates all NumberBox elements.- NumberBox: actually, a portable element.But for now it ondragEndremains empty. Let's fix this.First of all, let's go through the arguments of the method:result: { destination: { droppableId: "column-1" index: 2 } draggableId: "four" reason: "DROP" source: { droppableId: "column-1" index: 0 } type: "DEFAULT" }The argument contains data about the element being dragged (from which column and position) and the place where the element is being dragged (to which column and position), as well as whether the action is now “ drag ” or “ drop ”.First of all, save destination, draggableIdand sourceinto variables:const { destination, source, draggableId } = result;Now let's create a new list of sorted elements:const column = this.state.column; const numberIds = Array.from(column.numberIds); numberIds.splice(source.index, 1); numberIds.splice(destination.index, 0, draggableId); const numbers = numberIds.map((numberId: string) => parseInt(this.state.numbers[numberId].content, 10));And update the state:const newColumn = { ...column, numberIds }; this.setState({ ...this.state, column: newColumn });And the highlight of the game: as soon as the user drags an element or wins, the corresponding melody from the folder is played assets:public playSound(numbers: number[]) { const sound = isSortedAsc(numbers) ? ClapsSound : MoveSound; new Audio(sound).play(); }This is how the whole method looks like:public onDragEnd(result: any) { const { destination, source, draggableId } = result; if (!destination) { return } const column = this.state.column; const numberIds = Array.from(column.numberIds); numberIds.splice(source.index, 1); numberIds.splice(destination.index, 0, draggableId); const numbers = numberIds.map((numberId: string) => parseInt(this.state.numbers[numberId].content, 10)); this.playSound(numbers); this.updateState(column, numberIds); }ConclusionThose components that were described above are enough to create a group of elements with the possibility of dragging them within one column. If you add CSS and additional sounds to the game, it will make it more enjoyable.
How to develop your first app with React Native I want to talk a little about what React Native is, why it is important and popular now, and January 24, 2022 Tutorials
Important little things when developing mobile applications Probably, you have repeatedly searched the markets for a mobile application for some keyword, January 13, 2022 Blog
How to quickly write an Android game with Unity At the present time, anyone can become a successful mobile game or application developer without January 9, 2022 Tutorials