Drag and drop react typescript

React Drag and Drop Made Easy: A Step-by-Step Guide

In today’s article we are going to create a similar interface with applications like Trello or ClickUp. The idea is to create a foundation so that you can extend the functionality of the app. At the end of the article you will have a result similar to the following:

What are we going to use?

  • Stitches — a css-in-js styling library with phenomenal development experience
  • radash — a utility library that provides a set of functions that will help us deal with strings, objects and arrays
  • @dnd-kit/core — it’s the library we’re going to use to implement dnd, it’s intuitive, lightweight and it’s the new kid on the block

Bear in mind that although these are the libraries used in this article, the same result is also easily replicable with others.

Prerequisites

To follow this tutorial, you need:

You won’t have any problem if you don’t know TypeScript, you can always «ignore» the data types, however in today’s example it makes the whole process much easier.

Getting Started

As a first step, create a project directory and navigate into it:

yarn create vite react-dnd --template react-ts cd react-dnd 

Now we can install the necessary dependencies:

yarn add radash @dnd-kit/core @stitches/react @fontsource/anek-telugu 

With the boilerplate generated, we can start working on the application, but before we create the necessary components we need to pay attention to a few things.

Читайте также:  Using Where Clause

First, let’s create two primitives that will form the basis of two important components of today’s example. The first primitive will be the Droppable.tsx , which is basically the area that will contain several elements that will be draggable.

// @/src/primitives/Droppable.tsx import  FC, ReactNode, useMemo > from "react"; import  useDroppable > from "@dnd-kit/core"; interface IDroppable  id: string; children: ReactNode; > export const Droppable: FCIDroppable> = ( id, children >) =>  const  isOver, setNodeRef > = useDroppable( id >); const style = useMemo( () => ( opacity: isOver ? 0.5 : 1, >), [isOver] ); return ( div ref=setNodeRef> style=style>> children> /div> ); >; 

The other primitive that we will need is the Draggable.tsx that will be used in each of the elements that will be draggable.

// @/src/primitives/Draggable.tsx import  FC, ReactNode, useMemo > from "react"; import  useDraggable > from "@dnd-kit/core"; interface IDraggable  id: string; children: ReactNode; > export const Draggable: FCIDraggable> = ( id, children >) =>  const  attributes, listeners, setNodeRef, transform > = useDraggable( id >); const style = useMemo(() =>  if (transform)  return  transform: `translate3d($transform.x>px, $transform.y>px, 0)`, >; > return undefined; >, [transform]); return ( div ref=setNodeRef> style=style>  listeners>  attributes>> children> /div> ); >; 

You may have noticed that the two primitives we created have a prop in common, which is the id . Obviously they reference the identifier, different use cases:

  • In the case of Droppable.tsx the id corresponds to the stage of the task (backlog, in progress, etc).
  • While in Draggable.tsx it corresponds to the element id (can be an integer or uuid).

Now that we have the primitives we can work on the components that will use them. Starting with the simplest component, called DraggableElement.tsx will be responsible for rendering the contents of the task.

// @/src/components/DraggableElement.tsx import  FC, useMemo > from "react"; import  styled > from "@stitches/react"; import  Draggable > from "../primitives"; interface IDraggableElement  identifier: string; content: string; > export const DraggableElement: FCIDraggableElement> = ( identifier, content, >) =>  const itemIdentifier = useMemo(() => identifier, [identifier]); return ( Draggable id=itemIdentifier>> ElementWrapper> ElementText>content>/ElementText> /ElementWrapper> /Draggable> ); >; const ElementWrapper = styled("div",  background: "#f6f6f6", borderRadius: 10, height: 120, width: "100%", display: "flex", justifyContent: "center", alignItems: "center", marginTop: 12, >); const ElementText = styled("h3",  fontSize: 18, fontWeight: 600, >); 

The last component we are going to create is the Column.tsx , this will be responsible for rendering each of the elements that are associated with a specific task.

Speaking of this component, there is a very important element called which is a sensor, that is, it is this element that detects if the element being grabbed goes to a specific column/stage.

// @/src/components/Column.tsx import  FC, useMemo > from "react"; import  styled > from "@stitches/react"; import * as _ from "radash"; import  Droppable > from "../primitives"; import  DraggableElement > from "./DraggableElement"; export interface IElement  id: string; content: string; column: string; > interface IColumn  heading: string; elements: IElement[]; > export const Column: FCIColumn> = ( heading, elements >) =>  const columnIdentifier = useMemo(() => _.camal(heading), [heading]); const amounts = useMemo( () => elements.filter((elm) => elm.column === columnIdentifier).length, [elements, columnIdentifier] ); return ( ColumnWrapper> ColumnHeaderWrapper variant=columnIdentifier as any>> Heading>heading>/Heading> ColumnTasksAmout>amounts>/ColumnTasksAmout> /ColumnHeaderWrapper> Droppable id=columnIdentifier>> elements.map((elm, elmIndex) => ( DraggableElement key=`draggable-element-$elmIndex>-$columnIdentifier>`> identifier=elm.id> content=elm.content> /> ))> DropPlaceholder /> /Droppable> /ColumnWrapper> ); >; const Heading = styled("h3",  color: "#FFF", >); const ColumnWrapper = styled("div",  width: 320, padding: 10, border: "dashed", borderWidth: 2, borderRadius: 10, >); const DropPlaceholder = styled("div",  height: 35, backgroundColor: "transparent", marginTop: 15, >); const ColumnHeaderWrapper = styled("div",  display: "flex", flexDirection: "row", justifyContent: "space-between", alignItems: "center", variants:  variant:  backlog:  background: "#F94892", >, inProgress:  background: "#5800FF", >, inReview:  background: "#ffb300", >, done:  background: "#24A19C", >, >, >, padding: "0px 10px 0px 10px", borderRadius: 10, >); const ColumnTasksAmout = styled("span",  display: "flex", justifyContent: "center", alignItems: "center", width: 30, height: 30, borderRadius: 6, color: "#FFF", background: "rgba( 255, 255, 255, 0.25 )", boxShadow: "0 8px 32px 0 rgba( 255, 255, 255, 0.18 )", backdropFilter: "blur(5px)", border: "1px solid rgba( 255, 255, 255, 0.18 )", >); 

Last but not least, in App.tsx we will import the dependencies we need as well as define some variables and states related to the stages that we will have in the app.

Then we need to create a function called handleOnDragEnd() that will receive in the arguments the data related to the element that was dragged. In order to update it’s state (for example to change the stage of inProgress to inReview).

Finally, we can map each of the columns that correspond to each of the stages and we will pass the elements associated with them in the props of each of these columns.

import "@fontsource/anek-telugu"; import  useCallback, useState > from "react"; import  DndContext, DragEndEvent > from "@dnd-kit/core"; import  styled > from "@stitches/react"; import * as _ from "radash"; import  Column, IElement > from "./components"; const COLUMNS = ["Backlog", "In Progress", "In Review", "Done"]; export const DEFAULT_COLUMN = "backlog"; const DEFAULT_DATA_STATE: IElement[] = [  id: _.uid(6), content: "Hello world 1", column: DEFAULT_COLUMN, >,  id: _.uid(6), content: "Hello world 2", column: DEFAULT_COLUMN, >, ]; export const App = () =>  const [data, setData] = useStateIElement[]>(DEFAULT_DATA_STATE); const handleOnDragEnd = useCallback( ( active, over >: DragEndEvent) =>  const elementId = active.id; const deepCopy = [. data]; const updatedState = deepCopy.map((elm): IElement =>  if (elm.id === elementId)  const column = over?.id ? String(over.id) : elm.column; return  . elm, column >; > return elm; >); setData(updatedState); >, [data, setData] ); return ( DndContext onDragEnd=handleOnDragEnd>> MainWrapper> COLUMNS.map((column, columnIndex) => ( Column key=`column-$columnIndex>`> heading=column> elements=_.select( data, (elm) => elm, (f) => f.column === _.camal(column) )> /> ))> /MainWrapper> /DndContext> ); >; const MainWrapper = styled("div",  display: "flex", justifyContent: "space-evenly", backgroundColor: "#fff", paddingTop: 40, paddingBottom: 40, fontFamily: "Anek Telugu", height: "90vh", >); 

Conclusion

As usual, I hope you enjoyed the article and that it helped you with an existing project or simply wanted to try it out.

If you found a mistake in the article, please let me know in the comments so I can correct it. Before finishing, if you want to access the source code of this article, I leave here the link to the github repository.

Источник

React + TypeScript: Drag and Drop Example

This article walks you through a complete example of implementing drag and drop in a React application that uses TypeScript. We are going to write code from scratch and not using any third-party packages.

Example

In this example, we can drag the cute dog image (Image source: Pixabay) in the first box or drag the text in the second box (the yellow one) and drop it into the third box (the green).

Preview

The Code

1. Create a new React project by running:

npx create-react-app drag_drop_example --template typescript

2. Replace the default code in your src/App.jsx with the following:

// App.tsx // Kindacode.com import React, < useState >from "react"; import "./App.css"; const PHOTO_URL = "https://www.kindacode.com/wp-content/uploads/2021/06/cute-dog.jpeg"; const App = () => < // The content of the target box const [content, setContent] = useState("Drop Something Here"); // This function will be triggered when you start dragging const dragStartHandler = ( event: React.DragEvent, data: string ) => < event.dataTransfer.setData("text", data); >; // This function will be triggered when dropping const dropHandler = (event: React.DragEvent) => < event.preventDefault(); const data = event.dataTransfer.getData("text"); setContent(data); >; // This makes the third box become droppable const allowDrop = (event: React.DragEvent) => < event.preventDefault(); >; return ( 
dragStartHandler(event, PHOTO_URL)> draggable= > alt="Cute Dog" />
dragStartHandler(event, "Kindacode.com")> draggable= >

Kindacode.com

onDrop=> /> :

>

); >; export default App;

3. Remove all the unwanted code in your src/App.css and add this:

/* App.css Kindacode.com */ .container < padding: 50px; display: flex; justify-content: space-between; >.box1 < width: 320px; height: 240px; >img < max-width: 320px; >.box2 < width: 320px; height: 240px; background: yellow; display: flex; justify-content: center; align-items: center; >.box3

Conclusion

We’ve gone over a full example of dragging and dropping in React and TypeScript. If you would like to learn more about modern front-end development, take a look at the following articles:

You can also check our React category page and React Native category page for the latest tutorials and examples.

Источник

Оцените статью