ReactJS钩子-使用多个useState钩子和样式化组件拖放
我对钩子相当陌生,我正在尝试实现一个拖放容器组件,该组件在整个鼠标移动过程中处理onDragStart、onDrag和onDragEnd函数。我一直在尝试使用钩子复制这里找到的代码: 使用下面的代码,我几乎可以让它工作了。它使用样式化的组件设置动画。问题是,只有缓慢移动鼠标,它才能工作。如果快速移动鼠标,SVG或此div中包含的任何内容都将抛出屏幕 我有一个ReactJS钩子-使用多个useState钩子和样式化组件拖放,reactjs,styled-components,react-hooks,Reactjs,Styled Components,React Hooks,我对钩子相当陌生,我正在尝试实现一个拖放容器组件,该组件在整个鼠标移动过程中处理onDragStart、onDrag和onDragEnd函数。我一直在尝试使用钩子复制这里找到的代码: 使用下面的代码,我几乎可以让它工作了。它使用样式化的组件设置动画。问题是,只有缓慢移动鼠标,它才能工作。如果快速移动鼠标,SVG或此div中包含的任何内容都将抛出屏幕 我有一个component.js文件 import React, { useState, useEffect, useCallback } from
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
因此,我首先从父级传入初始值。我认为我没有正确处理useffect/useState,而且它获取信息的速度不够快
如果有人能帮我解决这个问题,我将不胜感激。再次道歉,但我对使用钩子很陌生
谢谢:)理想情况下,由于
setState
是异步的,因此您应该将所有状态移动到一个对象中(如中间示例所示)。然后,您可以利用确保调用设置状态时,每个事件侦听器
和事件回调
使用的值都是最新的
我认为那篇媒体文章中的例子也有同样的跳跃问题(这可能就是为什么这个例子视频移动物体的速度很慢),但是如果没有一个有效的例子,很难说。也就是说,为了解决这个问题,我删除了originalX
,originalY
,lastTranslateX
,lastTranslateY
值,因为我们利用了setState
回调,所以不需要这些值
此外,我将事件侦听器
/回调简化为:
mousedown
=>鼠标左键单击保持集isDragging
true
mousemove
=>通过clientX
和clientY
更新鼠标移动
mouseup
=>鼠标左键单击释放将isDraging
设置为false李>
这确保只有一个事件侦听器实际转换x
和y
值
如果要利用此示例包含多个圆,则需要重用下面的组件,或者使用useRef
并使用refs
移动选定的圆;然而,这超出了你最初问题的范围
最后,我还通过将styled.div.data.attr
重新构造为一个函数
,返回带有CSS
的样式
属性,修复了样式化组件
的弃用问题,而不是带有样式
属性的对象
,该属性是返回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)`
}
}))`
工作示例:
组件/圆
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;
组件/可拖动的
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;
import React,{useState,useffect,useCallback}来自“React”;
从“道具类型”导入道具类型;
从“./Circle”导入圆;
常量拖动=({位置,半径})=>{
常量[状态,设置状态]=使用状态({
IsDraging:错,
translateX:position.x,
translateY:position.y
});
//鼠标移动
const handleMouseMove=useCallback(
({clientX,clientY})=>{
if(state.isDraging){
设置状态(prevState=>({
…国家,
translateX:clientX,
translateY:clientY
}));
}
},
[国家统计局]
);
//鼠标左键单击释放
const handleMouseUp=useCallback(()=>{
if(state.isDraging){
设置状态(prevState=>({
…国家,
IsDraging:错误
}));
}
},[state.isDragging]);
//鼠标左键单击保持
const handleMouseDown=useCallback(()=>{
设置状态(prevState=>({
…国家,
伊斯特拉格:是的
}));
}, []);
//添加/清理鼠标事件侦听器
useffect(()=>{
window.addEventListener(“mousemove”,handleMouseMove);
window.addEventListener(“mouseup”,handleMouseUp);
return()=>{
window.removeEventListener(“mousemove”,handleMouseMove);
window.removeEventListener(“mouseup”,handleMouseUp);
};
},[handleMouseMove,handleMouseUp]);
返回(
);
};
//道具类型模式
Draggable.propTypes={
位置:PropTypes.shape({
x:PropTypes.number,
y:PropTypes.number
}),
半径:PropTypes.number
};
//如果未提供道具,则为默认道具
Draggable.defaultProps={
职位:{
x:20,
y:20
},
半径:10,
};
导出默认拖拽;
你能制作一个代码沙盒吗?这将更容易调试。这是codesandbox。。。它似乎不像我在故事书上做的那样平滑,而且增量似乎太大:S添加了一个修复程序,使数字正确添加。如果你试着几乎轻弹鼠标并快速移动它,圆形的div就会远离屏幕。非常感谢!这太神奇了。我非常感激。这一切都很有道理:)