React-dnd 使用 meteor 在 mongo 中拖动更新数据库,以便在刷新页面时保持顺序

React-dnd drag update db in mongo with meteor inorder to persist order when page is refreshed

我正在使用 react-dnd 拖放并有一个排序列表可以通过它进行映射,它有点工作我可以拖放并在刷​​新时看起来东西保持在正确的位置但是元素移动了一个行比我拖动它的地方。

主要问题是,当我拖放项目时,moveCardDb 中的卡片状态与函数外的卡片状态略有不同,为什么此时会有所不同我似乎无法理解出。

这是我的最小设置 https://codesandbox.io/s/gifted-goodall-qu43p?file=/src/Container.jsx

如果您在 moveCardDb 函数上查看控制台日志,您会看到卡片 stae 变量有点乱序

提前致谢

我有以下拖放代码

位置的映射函数和更新

  const [cards, setCards] = useState([]);
  let stateReplace = useMemo(() => {
    if (!isLoading && formBuilder?.inputs?.length) {
      return formBuilder.inputs;
    }
    return [];
  }, [isLoading]);

  useEffect(() => {
    setCards(stateReplace);
  }, [setCards, stateReplace]);

  // console.log(cards);

  const moveCard = useCallback(
    (dragIndex, hoverIndex) => {
      console.log(dragIndex);
      console.log(hoverIndex);
      const dragCard = cards[dragIndex];
      setCards(
        update(cards, {
          $splice: [
            [dragIndex, 1],
            [hoverIndex, 0, dragCard],
          ],
        })
      );
    },
    [cards]
  );

  const moveCardDb = useCallback(() => {
    //console.log(cards);

    Meteor.call("formLeadBuilderDrag.update", cards, params._id, function (
      error,
      result
    ) {
      console.log(result);
      console.log(error);
    });
  }, [cards]);

  const renderCard = (card, index) => {
    return (
      <>
        {isLoading ? (
          <div className="loading">loading...</div>
        ) : (
          <>
            <Card
              key={card.dragPositionId}
              index={index}
              id={card.dragPositionId}
              input={card.inputType}
              moveCard={moveCard}
              moveCardDb={moveCardDb}
            />
          </>
        )}
      </>
    );
  };
  return (
    <>
      {isLoading ? (
        <div className="loading">loading...</div>
      ) : (
        <form>
          <div style={style}>{cards.map((card, i) => renderCard(card, i))}</div>
          <input type="submit" />
        </form>
      )}
    </>
  );

渲染的卡片

import React, { useRef } from "react";
import { useDrag, useDrop } from "react-dnd";
import { ItemTypes } from "./ItemTypes";
const style = {
  border: "1px dashed gray",
  padding: "0.5rem 1rem",
  marginBottom: ".5rem",
  backgroundColor: "white",
  cursor: "move",
};
export const Card = ({ id, input, index, moveCard, moveCardDb }) => {
  const ref = useRef(null);
  const [, drop] = useDrop({
    accept: ItemTypes.CARD,
    hover(item, monitor) {
      if (!ref.current) {
        return;
      }
      const dragIndex = item.index;
      const hoverIndex = index;
      // Don't replace items with themselves
      if (dragIndex === hoverIndex) {
        return;
      }
      // Determine rectangle on screen
      const hoverBoundingRect = ref.current?.getBoundingClientRect();
      // Get vertical middle
      const hoverMiddleY =
        (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
      // Determine mouse position
      const clientOffset = monitor.getClientOffset();
      // Get pixels to the top
      const hoverClientY = clientOffset.y - hoverBoundingRect.top;
      // Only perform the move when the mouse has crossed half of the items height
      // When dragging downwards, only move when the cursor is below 50%
      // When dragging upwards, only move when the cursor is above 50%
      // Dragging downwards
      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return;
      }
      // Dragging upwards
      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return;
      }
      // Time to actually perform the action
      moveCard(dragIndex, hoverIndex);
      moveCardDb();
      // Note: we're mutating the monitor item here!
      // Generally it's better to avoid mutations,
      // but it's good here for the sake of performance
      // to avoid expensive index searches.
      item.index = hoverIndex;
    },
  });
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes.CARD, id, index },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  });
  const opacity = isDragging ? 0 : 1;
  drag(drop(ref));
  return (
    <div ref={ref} style={{ ...style, opacity }}>
      <p>{input}</p>
      <input
        name={input + id}
        defaultValue="test"
        // ref={register}
      />
      {/* <button type="button" onClick={onEditToggle}>
        <BiEditAlt size={25} />
      </button> */}
      {/* <button onClick={() => deleteLead(leads)}>&times;</button> */}
    </div>
  );
};

我的对象从头开始

{
  "_id": "showRoomId",
  "type": "Show Room Lead",
  "createdAt": "2020-11-14",
  "userId": "83nfd298dn382",
  "inputs": [
    {
      "inputType": "shortText",
      "dragPositionId": "1",
      "label": "First Name:"
    },
    {
      "inputType": "phoneNumber",
      "dragPositionId": "2",
      "label": "Cell Phone Number"
    },
    {
      "inputType": "email",
      "dragPositionId": "3",
      "label": "Work Email"
    },
    {
      "inputType": "Address",
      "dragPositionId": "4",
      "label": "Home Address"
    },
    {
      "inputType": "multipleChoice",
      "dragPositionId": "5",
      "label": "Preferred Method of Contact",
      "options": [
        {
          "dragPositionId": "1",
          "label": "Email"
        },
        {
          "dragPosition": "2",
          "label": "Cell Phone"
        }
      ]
    },
    {
      "inputType": "dropDown",
      "dragPositionId": "6",
      "label": "How did you find us?",
      "options": [
        {
          "dragPositionId": "1",
          "label": "Google"
        },
        {
          "dragPosition": "2",
          "label": "Referral"
        }
      ]
    }
  ]
}

你提供了一堆代码,很好,但我不清楚问题出在哪里,以及你试图尝试解决的问题。数据库中存储的东西是否如你所料,还是渲染问题?

我最终在 monitor.didDrop 函数的函数上花费了时间。这对我来说似乎有点老套,所以如果有人能提供更好的解决方案,请告诉我。我也决定先存储到localstorage,然后在表单提交时提交到数据库。

 const [{ isDragging, didDrop }, drag] = useDrag({
    item: { type: ItemTypes.CARD, id, index },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
      didDrop: monitor.didDrop(),
    }),
  });
  const opacity = isDragging ? 0 : 1;

  function droppy(dropped) {
    var delayInMilliseconds = 1000; //1 second

    setTimeout(function () {
      dropped();
    }, delayInMilliseconds);
  }

  if (didDrop) {
    droppy(moveCardDb);
  }