Reactsortablehoc-结合-hook-实现-Draggin-和-Droppin

13次阅读

共计 4135 个字符,预计需要花费 11 分钟才能阅读完成。

翻译:https://css-tricks.com/draggi…

React 社区提供了许多的库来实现拖放的功能,例如 react-dnd, react-beautiful-dnd, react-drag-n-drop,等等。但是它们有一些共同的缺陷:

  • 使用复杂,有时候需要做很多工作才能构建一个简单的拖放演示;
  • 功能有限,例如无法实现多个拖放实例这样复杂的功能,如果有的话,它也会变得非常复杂。

为了解决这些问题,react-sortable-hoc 应运而生。

这篇教程需要你提前了解一些 React 组件以及 hooks 的基础知识。

React 官方推荐的封装高阶组件的方式是 HOC,我们需要借助它来实现一个具有拖放功能的高阶组件。

HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

启动项目

教程最终的目的是构建一个带有趣 GIF 的应用程序(来自 Chris Gannon!),可以在视口周围拖动。具体见

首先,我们利用 create-react-app 来启动一个新的 React 项目:

npx create-react-app your-project-name

之后在项目中安装 react-sorting-hoc 以及 array-move,后者是用来实现数组移动。

cd your-project-name
yarn add react-sortable-hoc array-move

创建 GIF 组件,添加样式和数据

为简单起见,我们将在 App.css 文件中编写所有样式。你可以使用以下内容对既有样式进行覆盖:

.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;
}

接下来,让我们使用 React 的 useState hook 来实现 GIF 组件

import React, {useState} from 'react';

在 src 目录中创建一个 Gif.js 文件并写入以下代码:

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;

编写代码时尽力遵循最佳实践,因此我们还导入 PropTypes 进行类型检查。之后将 Gif 组件并将其添加到 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;

运行 npm run start,访问 http://localhost:3000/,可以看到如下结果

添加拖放功能

现在让我们对 Gif 组件添加拖拽功能。首先,我们需要了解 react-sortable-hoc 的两个 HOC,以及 array-move 的 arrayMove 方法,以便于在拖动发生修改数组。首先引入对应组件以及方法

import {sortableContainer, sortableElement} from 'react-sortable-hoc';
import arrayMove from 'array-move';

之前提到过 HOC,本质上 高阶组件是参数为组件,返回值为新组件的函数。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

可以看出,HOC 就是在当前 component 的外层包裹我们所需要实现的功能。所以 sortableContainersortableElement 就是 higherOrderComponent

  • sortableContainer 是所有可排序元素的容器;
  • sortableElement 是每个可渲染元素的容器。

import 之后我们要做的是

const SortableGifsContainer = sortableContainer(({children}) => <div className="gifs">{children}</div>);
    
const SortableGif = sortableElement(({gif}) => <Gif key={gif} gif={gif} />);

SortableGif 为每个子元素创建了一个容器,也就是为单个 Gif 组件创建。它们将使用在 SortableGifsContainer 中,以 children 属性传递进去

注:您需要将每个子项在 div 或任何其他有效的 HTML 元素中。

然后用新创建的 SortableGif 替换原有的 Gif 组件,并在 SortableGifsContainer 中使用。

<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>

请务必注意,您需要将 index prop 传递给可排序元素,以便库可以区分每一个子项目。它类似于在 React 中向列表添加键。

SortableGifsContainer 上,我们添加了 axis="x",是因为 Gif 组件是水平放置的,如果想要水平拖动它们就需要配置 axis,而默认是垂直拖动。换句话说,axis="x" 限制子项目只能沿水平 x 轴拖放。同时,还添加了 onSortEnd={onSortEnd},该函数在每次拖动或排序项目时触发。它的实现如下

const onSortEnd = ({oldIndex, newIndex}) => setGifs(arrayMove(gifs, oldIndex, newIndex));

onSortEnd 接收了一个被拖动的项目的新旧 index,当然,每次我们移动项目后,我们都会在 arrayMove 的帮助下修改数据。

现在已经知道如何在项目中实现拖放!????????????

如果我们有多个项目列表怎么办?

之前的例子是为了展示 react-sortable-hoc 的功能,所以非常的清晰以及简单。但是在实际的业务中,你可能会遇到多个项目列表都需要拖放的场景,那该怎么办?

react-sortable-hoc 为我们提供了一个 collection prop 用来区分列表。在上一个例子的基础上,我们添加一个新的列表

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',
]);

和之前一样,除了需要在 SortableGif 组件上添加 collection props 具体如下

<h2>Set 2</h2>

<SortableGifsContainer axis="x" onSortEnd={onSortEnd}>
    {newGifs.map((gif, i) => (
        <SortableGif
            index={i}
            key={gif}
            gif={gif}
            collection="newGifs"
        />
    ))}
</SortableGifsContainer>

在之前的第一个列表中也添加相应的 collection 属性,例如 collection="gifs"

之后修改 onSortEnd 函数,它的参数中会多一个 collection

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;
  }
}

然后,我们就可以自由的拖放了。

如图中所示,我们现在有两个单独的 GIF 列表,可以自由拖放。而且,它们是独立的意思,来自不同列表的项目不会混淆。

正文完
 0