javascript - React-dnd 使用流星在 mongo 中拖动更新数据库,以便在页面刷新时保持顺序
问题描述
我正在使用 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)}>×</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 函数上的一个函数上投入了时间。这对我来说似乎有点 hacky,所以如果有人可以提供更好的解决方案,请告诉我。我还决定先存储到本地存储,然后在表单提交时提交到数据库。
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);
}
推荐阅读
- if-statement - 用于条件格式自定义函数的 Countblank 和 ImportRange
- html - 如何使文本在 CSS 中的框中居中?我的没用
- node.js - Google App Engine - 部署 nodejs 应用程序时缺少目录
- excel - 希望返回给定公司名称的行的最大值
- html - '已添加具有相同密钥的项目'
- three.js - 将 MeshStandardMaterial 复制到 ShaderMaterial 时如何渲染 PMREM 环境贴图
- entity-framework - 在 select LINQ 子句和方法中创建新对象有什么区别
- join - 关系代数-fullouterjoin
- sql-server - 使用变量表将多条记录插入/合并到一条记录中以打印在明信片的两面
- android - 具有约束布局的 NestedScrollView 作为子项不滚动