Translation: https://css-tricks.com/draggi...
The React community provides many libraries to implement drag-and-drop functions, such as react-dnd, react-beautiful-dnd, react-drag-n-drop Wait a minute. But they have some common shortcomings:
- It's complex to use, and sometimes it takes a lot of work to build a simple drag-and-drop demonstration.
- Limited functionality, such as the inability to implement complex functions such as multiple drag-and-drop instances, can become very complex if there is one.
In order to solve these problems, react-sortable-hoc Emerge as the times require.
This tutorial requires you to know some basic knowledge of React components and hooks in advance.
React officially recommends HOC as the way to encapsulate high-order components. We need to use it to implement a high-order component with drag-and-drop function.
HOC itself is not a part of React API. It is a design pattern based on the combination characteristics of React.
Start the project
The ultimate goal of the tutorial is to build an application with interesting GIF (from Chris Gannon!). It can be dragged around the viewport. Specific Views
First, we use create-react-app to start a new React project:
npx create-react-app your-project-name
Then react-sorting-hoc and array-move are installed in the project, which is used to achieve array movement.
cd your-project-name yarn add react-sortable-hoc array-move
Create GIF components, add styles and data
For simplicity, we will write all styles in the App.css file. You can use the following to cover existing styles:
.App { background: #1a1919; color: #fff; min-height: 100vh; padding: 25px; text-align: center; } .App h1 { font-size: 52px; margin: 0; } .App h2 { color: #f6c945; text-transform: uppercase; } .App img { cursor: grab; height: 180px; width: 240px; }
Next, let's use React's useState hook to implement GIF components
import React, { useState } from 'react';
Create a Gif.js file in the src directory and write the following code:
import React from 'react'; import PropTypes from 'prop-types'; const Gif = ({ gif }) => (<img src={gif} alt="gif" />) Gif.propTypes = { gif: PropTypes.string.isRequired, }; export default Gif;
We try to follow best practices when writing code, so we also import PropTypes for type checking. Then add the Gif component to App
import React, { useState } from 'react'; import './App.css'; import Gif from './Gif'; const App = () => { const [gifs, setGifs] = useState(['https://media.giphy.com/media/3ohhwoWSCtJzznXbuo/giphy.gif','https://media.giphy.com/media/l46CbZ7KWEhN1oci4/giphy.gif','https://media.giphy.com/media/3ohzgD1wRxpvpkDCSI/giphy.gif','https://media.giphy.com/media/xT1XGYy9NPhWRPp4pq/giphy.gif',]); return ( <div className="App"> <h1>Drag those GIFs around</h1> <h2>Set 1</h2> {gifs.map((gif, i) => <Gif key={gif} gif={gif} />)} </div> ); } export default App;
Run npm run start, access http://localhost 3000/, you can see the following results
Add drag-and-drop functionality
Now let's add drag and drop functionality to the Gif component. First, we need to understand the two HOCs of react-sortable-hoc and the arrayMove method of array-move in order to modify the array when dragging. Firstly, corresponding components and methods are introduced.
import { sortableContainer, sortableElement } from 'react-sortable-hoc'; import arrayMove from 'array-move';
As mentioned earlier, HOC is essentially a function whose parameters are components and whose return values are new components.
const EnhancedComponent = higherOrderComponent(WrappedComponent);
As you can see, HOC is just wrapping the functions we need to implement outside the current component. So sortable Container, sortable Element, is the higherOrderComponent.
- Sortable Container is the container for all sortable elements.
- Sortable Element is a container for each renderable element.
What we need to do after import is
const SortableGifsContainer = sortableContainer(({ children }) => <div className="gifs">{children}</div>); const SortableGif = sortableElement(({ gif }) => <Gif key={gif} gif={gif} />);
SortableGif creates a container for each child element, that is, for a single Gif component. They will be used in SortableGifsContainer and passed in with the child attribute
Note: You need to place each subitem in a div or any other valid HTML element.
Then replace the original Gif component with the newly created Portable Gif and use it in Sortable Gifs Container.
<SortableGifsContainer axis="x" onSortEnd={onSortEnd}> {gifs.map((gif, i) => <SortableGif // don't forget to pass index prop with item index index={i} key={gif} gif={gif} /> )} </SortableGifsContainer>
It's important to note that you need to pass index prop to sortable elements so that the library can distinguish each subitem. It's similar to adding keys to a list in React.
On Portable Gifs Container, we added axis="x" because Gif components are placed horizontally, and if you want to drag them horizontally, you need to configure axis, which defaults to vertical dragging. In other words, axis="x" restricts that subitems can only be dragged along the horizontal x axis. At the same time, onSortEnd={onSortEnd} is added, which is triggered every time an item is dragged or sorted. Its implementation is as follows
const onSortEnd = ({ oldIndex, newIndex }) => setGifs(arrayMove(gifs, oldIndex, newIndex));
onSortEnd receives an old and new index for a dragged project. Of course, every time we move a project, we modify the data with the help of arrayMove.
Now you know how to drag and drop in a project! Rising and rolling
What if we have multiple item lists?
The previous example is to show the function of react-sortable-hoc, so it is very clear and simple. But in real business, you may encounter scenarios where multiple item lists need to be dragged and dropped. What should you do?
react-sortable-hoc provides us with a collection prop to distinguish lists. On the basis of the previous example, we add a new list
const [newGifs, setNewGifs] = useState([ 'https://media.giphy.com/media/xiOgHgY2ceKhm46cAj/giphy.gif', 'https://media.giphy.com/media/3oKIPuMqYfRsyJTWfu/giphy.gif', 'https://media.giphy.com/media/4ZgLPakqTajjVFOVqw/giphy.gif', 'https://media.giphy.com/media/3o7btXIelzs8nBnznG/giphy.gif', ]);
As before, except for the need to add collection props to the SortableGif component
<h2>Set 2</h2> <SortableGifsContainer axis="x" onSortEnd={onSortEnd}> {newGifs.map((gif, i) => ( <SortableGif index={i} key={gif} gif={gif} collection="newGifs" /> ))} </SortableGifsContainer>
Add the corresponding collection attribute to the previous first list, such as collection="gifs"
The onSortEnd function is then modified to include an additional collection in its parameters.
const onSortEnd = ({ oldIndex, newIndex, collection }) => { switch(collection) { case 'gifs': setGifs(arrayMove(gifs, oldIndex, newIndex)) break; case 'newGifs': setNewGifs(arrayMove(newGifs, oldIndex, newIndex)) break; default: break; } }
Then we can drag and drop freely.
As shown in the figure, we now have two separate GIF lists that we can drag and drop freely. Moreover, they are independent meanings, and items from different lists are not confused.