react-sortable-hoc 小demo

177 阅读1分钟

react拖拽 - react-sortable-hoc

推荐react18以下版本使用

image.png

npm install react-sortable-hoc@1.9.1 array-move@1.0.0 -D

简单理解就是: 包含在SortableList的SortableItem才可以拖拽,拖拽完通过array-move库对其进行重新排序.

模仿公众号配置页面开发

import { useState } from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import arrayMove from 'array-move';
import styles from './index.less';
import { message } from 'antd';
import { nanoid } from 'nanoid';

export default function SortableComponent() {
  const [items1, setItems1] = useState([
    { name: '服务咨询', id: nanoid(10) },
    { name: '公司动态', id: nanoid(10) },
  ]);
  const [items2, setItems2] = useState([{ name: '在线办事', id: nanoid(10) }]);
  const [items3, setItems3] = useState([{ name: '需求留言', id: nanoid(10) }]);
  const [current, setCurrent] = useState('');

  // 子项
  const SortableItem: any = SortableElement(({ value }: any) => {
    return (
      <div
        className={styles.item}
        tabIndex={0}
        onClick={() => {
          setCurrent(value.id);
        }}
        style={{
          backgroundColor: value.id == current ? '#eee' : '',
        }}
      >
        {value.name}
      </div>
    );
  });

  // 列表
  const SortableList1: any = SortableContainer(({ items }: any) => {
    return (
      <div>
        {items.map((value: any, index: number) => (
          <SortableItem key={`item-${value.id}`} index={index} value={value} />
        ))}
      </div>
    );
  });
  const SortableList2: any = SortableContainer(({ items }: any) => {
    return (
      <div>
        {items.map((value: any, index: number) => (
          <SortableItem key={`item-${value.id}`} index={index} value={value} />
        ))}
      </div>
    );
  });
  const SortableList3: any = SortableContainer(({ items }: any) => {
    return (
      <div>
        {items.map((value: any, index: number) => (
          <SortableItem key={`item-${value.id}`} index={index} value={value} />
        ))}
      </div>
    );
  });

  function onSortEnd1({ oldIndex, newIndex }: any) {
    let oldItems = [...items1];
    let newArrs = arrayMove(oldItems, oldIndex, newIndex);
    setItems1(newArrs);
  }
  function onSortEnd2({ oldIndex, newIndex }: any) {
    let oldItems = [...items2];
    let newArrs = arrayMove(oldItems, oldIndex, newIndex);
    setItems2(newArrs);
  }
  function onSortEnd3({ oldIndex, newIndex }: any) {
    let oldItems = [...items3];
    let newArrs = arrayMove(oldItems, oldIndex, newIndex);
    setItems3(newArrs);
  }

  // 添加接口
  function add(type: number) {
    setCurrent('');
    if (type == 1) {
      if (items1.length < 5) {
        let newItem1 = [...items1];
        newItem1.push({
          name: '子菜单名称',
          id: nanoid(10),
        });
        setItems1(newItem1);
      } else {
        message.info('只能添加5个!');
      }
    }

    if (type == 2) {
      if (items2.length < 5) {
        let newItem2 = [...items2];
        newItem2.push({
          name: '子菜单名称',
          id: nanoid(10),
        });
        setItems2(newItem2);
      } else {
        message.info('只能添加5个!');
      }
    }

    if (type == 3) {
      if (items3.length < 5) {
        let newItem3 = [...items3];
        newItem3.push({
          name: '子菜单名称',
          id: nanoid(10),
        });
        setItems3(newItem3);
      } else {
        message.info('只能添加5个!');
      }
    }
  }

  return (
    <div className={styles.box}>
      <div>公众号菜单</div>

      <div className={styles['bottom-box']}>
        <div className={styles['bottom-box-item']}>
          <SortableList1 distance={1} items={items1} onSortEnd={onSortEnd1} />
          <div
            className={styles.addBtn}
            onClick={() => {
              add(1);
            }}
          >
            +
          </div>
          <div
            className={styles['bottomBar']}
            onClick={() => {
              setCurrent('one');
            }}
            style={{
              backgroundColor: 'one' == current ? '#eee' : '',
            }}
          >
            首页
          </div>
        </div>
        <div
          className={styles['bottom-box-item']}
          style={{
            left: 100,
          }}
        >
          <SortableList2 distance={1} items={items2} onSortEnd={onSortEnd2} />
          <div
            className={styles.addBtn}
            onClick={() => {
              add(2);
            }}
          >
            +
          </div>
          <div
            className={styles['bottomBar']}
            onClick={() => {
              setCurrent('two');
            }}
            style={{
              backgroundColor: 'two' == current ? '#eee' : '',
            }}
          >
            关于公司
          </div>
        </div>
        <div
          className={styles['bottom-box-item']}
          style={{
            left: 200,
          }}
        >
          <SortableList3 distance={1} items={items3} onSortEnd={onSortEnd3} />
          <div
            className={styles.addBtn}
            onClick={() => {
              add(3);
            }}
          >
            +
          </div>
          <div
            className={styles['bottomBar']}
            onClick={() => {
              setCurrent('three');
            }}
            style={{
              backgroundColor: 'three' == current ? '#eee' : '',
            }}
          >
            联系我们
          </div>
        </div>
      </div>
    </div>
  );
}

.item {
  width: 100px;
  height: 50px;
  line-height: 50px;
  text-align: center;
  border: 1px solid #d0d0d0;
  cursor: pointer;
  &:hover {
    border: 1px solid #44b549;
  }
}

.box {
  position: relative;
  width: 300px;
  height: 500px;
  margin: 0 0 30px 30px;
  background-color: #fff;
  border: 1px solid #d0d0d0;
  user-select: none;
}

.addBtn {
  position: relative;
  width: 100px;
  height: 50px;
  font-size: 20px;
  line-height: 50px;
  text-align: center;
  border: 1px solid #d0d0d0;
  &:hover {
    border: 1px solid #44b549;
  }
}

.bottom-box {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  display: flex;

  .bottom-box-item {
    position: absolute;
    bottom: 0;
    width: 100px;
    cursor: pointer;
    .bottomBar {
      flex: 1;
      height: 50px;
      line-height: 50px;
      text-align: center;

      border: 1px solid #d0d0d0;
      &:hover {
        border: 1px solid #44b549;
      }
    }
  }
}
image.png

简单版本

import { useState } from "react";
import { SortableContainer, SortableElement } from "react-sortable-hoc";
import arrayMove from "array-move";
import { nanoid } from "nanoid";

export default function SortableComponent() {
  const [items, setItems] = useState([
    { name: "item1", id: nanoid(10) },
    { name: "item2", id: nanoid(10) },
  ]);

  // 子项
  const SortableItem = SortableElement(({ value }) => {
    return (
      <div
        style={{
          width: "100px",
          height: "50px",
          lineHeight: "50px",
          border: "1px solid #d0d0d0",
          cursor: "pointer",
          textAlign: "center",
          marginTop: "10px",
        }}
        tabIndex={0}
      >
        {value.name}
      </div>
    );
  });
  // 列表
  const SortableList = SortableContainer(({ items }) => {
    return (
      <div>
        {items.map((value, index) => (
          <SortableItem key={`item-${value.id}`} index={index} value={value} />
        ))}
      </div>
    );
  });

  function onSortEnd({ oldIndex, newIndex }) {
    let oldItems = [...items];
    let newArrs = arrayMove(oldItems, oldIndex, newIndex);
    setItems(newArrs);
  }
  // distance={8} 拖拽8px才触发
  return <SortableList distance={1} items={items} onSortEnd={onSortEnd} />;
}

原理都类似. 推荐使用dnd-kit,因为react-sortable-hoc不支持react18

在react18中使用会出现下面的问题(重叠的问题)

image.png