gpt4 book ai didi

ReactJS 钩子(Hook) - 拖放多个 useState 钩子(Hook)和样式组件

转载 作者:行者123 更新时间:2023-12-05 08:52:37 27 4
gpt4 key购买 nike

我对钩子(Hook)相当陌生,我正在尝试实现一个拖放容器组件,该组件在整个鼠标移动过程中处理 onDragStart、onDrag 和 onDragEnd 函数。我一直在尝试使用钩子(Hook)复制此处找到的代码:https://medium.com/@crazypixel/mastering-drag-drop-with-reactjs-part-01-39bed3d40a03

我几乎已经使用下面的代码让它工作了。它使用样式化的组件进行动画处理。问题是它只有在您缓慢移动鼠标时才有效。如果您快速移动鼠标,SVG 或此 div 中包含的任何内容都会被抛出屏幕。

我有一个 component.js 文件,看起来像

import React, { useState, useEffect, useCallback } from 'react';
import { Container } from './style'

const Draggable = ({children, onDragStart, onDrag, onDragEnd, xPixels, yPixels, radius}) => {
const [isDragging, setIsDragging] = useState(false);
const [original, setOriginal] = useState({
x: 0,
y: 0
});
const [translate, setTranslate] = useState({
x: xPixels,
y: yPixels
});
const [lastTranslate, setLastTranslate] = useState({
x: xPixels,
y: yPixels
});

useEffect(() =>{
setTranslate({
x: xPixels,
y: yPixels
});
setLastTranslate({
x: xPixels,
y: yPixels
})
}, [xPixels, yPixels]);

const handleMouseMove = useCallback(({ clientX, clientY }) => {

if (!isDragging) {
return;
}
setTranslate({
x: clientX - original.x + lastTranslate.x,
y: clientY - original.y + lastTranslate.y
});
}, [isDragging, original, lastTranslate, translate]);



const handleMouseUp = useCallback(() => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp);

setOriginal({
x:0,
y:0
});
setLastTranslate({
x: translate.x,
y: translate.y
});

setIsDragging(false);
if (onDragEnd) {
onDragEnd();
}

}, [isDragging, translate, lastTranslate]);

useEffect(() => {
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);

return () => {
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp)
};
}, [handleMouseMove, handleMouseUp]);

const handleMouseDown = ({ clientX, clientY }) =>{

if (onDragStart) {
onDragStart();
}
setOriginal({
x: clientX,
y: clientY
});
setIsDragging(true);
};

return(
<Container
onMouseDown={handleMouseDown}
x={translate.x}
y={translate.y}
{...{radius}}
isDragging={isDragging}
>
{children}
</Container>
)
};

export default Draggable

样式组件文件 styled.js 如下所示:

import styled from 'styled-components/macro';

const Container = styled.div.attrs({
style: ({x,y, radius}) => ({
transform: `translate(${x - radius}px, ${y - radius}px)`
})
})`
//cursor: grab;
position: absolute;

${({isDragging}) =>
isDragging && `

opacity: 0.8
cursor: grabbing
`}
`;

export {
Container
}

所以我最初从父级传递初始值。我认为我没有正确处理 useEffect/useState 并且它获取信息的速度不够快。

如果有人能帮我弄清楚如何解决这个问题,我将不胜感激。再次抱歉,我对使用钩子(Hook)还很陌生。

谢谢你:)

最佳答案

理想情况下,由于 setState 是异步的,您可以将所有状态移动到一个 object 中(如中例所示)。然后,您可以利用 setState callback确保每个 event listenerevent callback 使用的值在调用 setState 时都是最新的。

我认为该媒体文章中的示例存在相同的跳跃问题(这可能是示例视频缓慢移动对象的原因),但如果没有工作示例,则很难说。也就是说,为了解决这个问题,我删除了 originalXoriginalYlastTranslateXlastTranslateY 值,因为它们是不需要,因为我们正在利用 setState 回调。

此外,我将事件监听器/回调简化为:

  • mousedown => 鼠标左键单击按住设置 isDragging true
  • mousemove => 鼠标移动通过clientXclientY 更新translateXtranslateY > 更新
  • mouseup => 鼠标左键单击释放设置 isDragging 为 false。

这确保只有一个事件监听器实际转换 xy 值。

如果你想利用这个例子来包含多个圆圈,那么你需要重用下面的组件或者使用 useRef 并利用 refs 来移动选择的圆圈;但是,这超出了您最初问题的范围。

最后,我还通过将 styled.div.data.attr 重组为一个 function 来解决 styled-components 弃用问题返回带有 CSSstyle 属性,而不是带有 style 属性的 object 是一个 函数 返回 CSS

已弃用:

styled.div.attrs({
style: ({ x, y, radius }) => ({
transform: `translate(${x - radius}px, ${y - radius}px)`
})
})`

更新:

styled.div.attrs(({ x, y, radius }) => ({
style: {
transform: `translate(${x - radius}px, ${y - radius}px)`
}
}))`

工作示例:

Edit Drag and Drop Example


组件/圆

import styled from "styled-components";

const Circle = styled.div.attrs(({ x, y, radius }) => ({
style: {
transform: `translate(${x - radius}px, ${y - radius}px)`
}
}))`
cursor: grab;
position: absolute;
width: 25px;
height: 25px;
background-color: red;
border-radius: 50%;

${({ isDragging }) =>
isDragging &&
`
opacity: 0.8;
cursor: grabbing;
`}
`;

export default Circle;

组件/可拖动

import React, { useState, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
import Circle from "../Circle";

const Draggable = ({ position, radius }) => {
const [state, setState] = useState({
isDragging: false,
translateX: position.x,
translateY: position.y
});

// mouse move
const handleMouseMove = useCallback(
({ clientX, clientY }) => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
translateX: clientX,
translateY: clientY
}));
}
},
[state.isDragging]
);

// mouse left click release
const handleMouseUp = useCallback(() => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
isDragging: false
}));
}
}, [state.isDragging]);

// mouse left click hold
const handleMouseDown = useCallback(() => {
setState(prevState => ({
...prevState,
isDragging: true
}));
}, []);

// adding/cleaning up mouse event listeners
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);

return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [handleMouseMove, handleMouseUp]);

return (
<Circle
isDragging={state.isDragging}
onMouseDown={handleMouseDown}
radius={radius}
x={state.translateX}
y={state.translateY}
/>
);
};

// prop type schema
Draggable.propTypes = {
position: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
radius: PropTypes.number
};

// default props if none are supplied
Draggable.defaultProps = {
position: {
x: 20,
y: 20
},
radius: 10,
};

export default Draggable;

关于ReactJS 钩子(Hook) - 拖放多个 useState 钩子(Hook)和样式组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56013531/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com