bounty将在4天后过期。回答此问题可获得+400声望奖励。Kamran Asgari希望吸引更多人关注此问题。
我在一个map循环中使用了ref。我需要一个ref数组。问题是ref只针对列表中生成的最后一个元素。这里是我准备的示例,我需要在map循环中通过ref列表对所有生成的元素运行自定义钩子。
我正在寻找一种方法而不引入其他组件
import React, { useRef, useState, useEffect, useCallback } from "react";
/// throttle.ts
export const throttle = (f) => {
let token = null,
lastArgs = null;
const invoke = () => {
f(...lastArgs);
token = null;
};
const result = (...args) => {
lastArgs = args;
if (!token) {
token = requestAnimationFrame(invoke);
}
};
result.cancel = () => token && cancelAnimationFrame(token);
return result;
};
const id = (x) => x;
const useDraggable = ({ onDrag = id } = {}) => {
const [pressed, setPressed] = useState(false);
const position = useRef({ x: 0, y: 0 });
const ref = useRef();
const unsubscribe = useRef();
const legacyRef = useCallback((elem) => {
ref.current = elem;
if (unsubscribe.current) {
unsubscribe.current();
}
if (!elem) {
return;
}
const handleMouseDown = (e) => {
e.target.style.userSelect = "none";
setPressed(true);
};
elem.addEventListener("mousedown", handleMouseDown);
unsubscribe.current = () => {
elem.removeEventListener("mousedown", handleMouseDown);
};
}, []);
useEffect(() => {
if (!pressed) {
return;
}
const handleMouseMove = throttle((event) => {
if (!ref.current || !position.current) {
return;
}
const pos = position.current;
const elem = ref.current;
position.current = onDrag({
x: pos.x + event.movementX,
y: pos.y + event.movementY
});
elem.style.transform = `translate(${pos.x}px, ${pos.y}px)`;
});
const handleMouseUp = (e) => {
e.target.style.userSelect = "auto";
setPressed(false);
};
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
return () => {
handleMouseMove.cancel();
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [pressed, onDrag]);
return [legacyRef, pressed];
};
/// example.ts
const quickAndDirtyStyle = {
width: "200px",
height: "200px",
background: "#FF9900",
color: "#FFFFFF",
display: "flex",
justifyContent: "center",
alignItems: "center"
};
const DraggableComponent = () => {
const handleDrag = useCallback(
({ x, y }) => ({
x: Math.max(0, x),
y: Math.max(0, y)
}),
[]
);
const [ref, pressed] = useDraggable({
onDrag: handleDrag
});
return (
<>
{[1, 2, 3].map((el, i) => (
<div key={"element" + i} ref={ref} style={quickAndDirtyStyle}>
<p>{pressed ? "Dragging..." : "Press to drag"}</p>
</div>
))}
</>
);
};
export default function App() {
return (
<div className="App">
<DraggableComponent />
</div>
);
}
到代码沙箱的链接在这里是https://codesandbox.io/s/determined-wave-pfklec?file=/src/App.js
3条答案
按热度按时间eulz3vhy1#
假设目标是使每个生成的元素都可以单独拖动,下面是一个示例,将一些
ref
切换为数组,并将pressed
更改为number | boolean
以传递index
。将
legacyRef
和pressed
的名称更改为handleRefs
和pressedIndex
,以反映其用例的差异。分叉现场演示:codesandbox(已更新以省略使用
useCallback
)但是,应用了挂钩后,似乎每个元素(除了第一个元素)的可拖动区域都有限。
发布的示例在第三个可拖动项上也有这种行为,所以不确定这是否是钩子的意图。如果不是,可能需要调整可拖动项的实现以适合所有元素。
希望能起到一定的参考作用。
iqih9akk2#
在useDraggable函数中,创建一个引用数组:
在handleMouseMove中使用此数组:
我们的想法是使用数组索引将ref赋给每个元素。
wvt8vs2t3#
您可以像这样使用ref。
像这样使用它。
ref={(element) => (flatlistRef[index] = element)}
你也可以像这样使用map函数。
ref.map(async (item, index) => {})