[英]Change width on mousedown and mousemove in React
I want to build a hook that changes the width of an element on mousemove
and mousedown
events.我想构建一个钩子,在mousemove
和mousedown
事件上更改元素的宽度。
I'm using the following code (that is actually working):我正在使用以下代码(实际上是有效的):
import React, { useState, useCallback, useEffect, useRef } from "https://cdn.skypack.dev/react";
import ReactDOM from "https://cdn.skypack.dev/react-dom";
const useMouseDelta = (initialWidth) => {
const [dragging, setDragging] = useState(false);
const [result, setResult] = useState(initialWidth);
const origin = useRef(initialWidth);
const handleMouseDown = useCallback((e) => {
origin.current = e.clientX;
setDragging(true);
}, []);
const handleMouseUp = useCallback((e) => {
setDragging(false);
}, []);
const handleMouseMove = useCallback((e) => {
if (!dragging) {
return;
}
setResult(result + (e.clientX - origin.current));
},
[dragging]
);
useEffect(() => {
window.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mouseup', handleMouseUp);
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousedown', handleMouseDown);
window.removeEventListener('mouseup', handleMouseUp);
window.removeEventListener('mousemove', handleMouseMove);
};
}, [dragging]);
return result;
};
const Test = () => {
const width = useMouseDelta(400);
return (
<div className="container" style={{width: width}}>
<div className="resize"></div>
</div>
)
}
ReactDOM.render(<Test />, document.getElementById('root'));
However, I'm seeing this warning in ESLint:但是,我在 ESLint 中看到了这个警告:
React Hook useCallback has a missing dependency: 'result'. Either include it or remove the dependency array. You can also do a functional update 'setResult(r => ...)' if you only need 'result' in the 'setResult' call
So I change it to:所以我把它改成:
setResult(r => r + (e.clientX - origin.current));
Now the drag is not working as desired anymore.现在拖动不再按预期工作。
An example can be found here: CodePen example可以在此处找到示例: CodePen 示例
In this Effect you can add all the dependencies like this:在此 Effect 中,您可以添加所有依赖项,如下所示:
useEffect(() => {
window.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mouseup', handleMouseUp);
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousedown', handleMouseDown);
window.removeEventListener('mouseup', handleMouseUp);
window.removeEventListener('mousemove', handleMouseMove);
};
}, [dragging, handleMouseDown, handleMouseUp, handleMouseMove]);
In the handleMouseMove it's a bit more tricky.在 handleMouseMove 中,它有点棘手。 It's ommited on purpose, so that it will only see the 'currentWidth' of the component and not the updates versions.它是故意省略的,因此它只会看到组件的“currentWidth”而不是更新版本。 The setResult can be used like this: setResult 可以这样使用:
const handleMouseMove = useCallback((e) => {
if (!dragging) {
return;
}
setResult((result) => result + (e.clientX - origin.current));
},
[dragging]
);
Then the warning will not come up, but you will see that it will always see the updated value and it will cause extra additions.然后警告不会出现,但您会看到它总是会看到更新的值,并且会导致额外的添加。
A solution would be to store the current witdth in a different state like this in useMouseDelta and then it can be added as a dependency:一个解决方案是在 useMouseDelta 中以不同的状态存储当前的宽度,然后可以将其添加为依赖项:
const useMouseDelta = (initialWidth: number) => {
// The distance the mouse has moved since `mousedown`.
const [dragging, setDragging] = useState(false);
const [result, setResult] = useState(initialWidth);
const [currentWidth, setCurrentWidth] = useState(initialWidth);
// Original position independent of any dragging. Updated when done dragging.
const origin = useRef(initialWidth);
const handleMouseDown = useCallback((e) => {
origin.current = e.clientX;
setDragging(true);
}, []);
const handleMouseUp = useCallback(
(e) => {
setDragging(false);
setCurrentWidth(result);
},
[result]
);
const handleMouseMove = useCallback(
(e) => {
if (!dragging) {
return;
}
setResult(() => currentWidth + e.clientX - origin.current);
},
[currentWidth, dragging]
);
useEffect(() => {
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mouseup", handleMouseUp);
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mouseup", handleMouseUp);
window.removeEventListener("mousemove", handleMouseMove);
};
}, [dragging, handleMouseDown, handleMouseUp, handleMouseMove]);
return result;
};
origin
, but you need to store previousClientX
with useRef()
and add just a change of clientX
between two events.您不需要存储origin
,但您需要使用useRef()
存储previousClientX
并仅在两个事件之间添加clientX
的更改。dragging
with useState()
, useRef()
is enough.您不需要使用useState()
存储dragging
, useRef()
就足够了。useEffect()
.您需要将所有处理程序添加到useEffect()
的依赖项列表。The improved code :改进后的代码:
import React, {
useState,
useCallback,
useEffect,
useRef
} from "https://cdn.skypack.dev/react";
import ReactDOM from "https://cdn.skypack.dev/react-dom";
const useMouseDelta = (initialWidth) => {
const [result, setResult] = useState(initialWidth);
const dragging = useRef(false);
const previousClientX = useRef(0);
const handleMouseMove = useCallback((e) => {
if (!dragging.current) {
return;
}
setResult((result) => {
const change = e.clientX - previousClientX.current;
previousClientX.current = e.clientX;
return result + change;
});
}, []);
const handleMouseDown = useCallback((e) => {
previousClientX.current = e.clientX;
dragging.current = true;
}, []);
const handleMouseUp = useCallback((e) => {
dragging.current = false;
}, []);
useEffect(() => {
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mouseup", handleMouseUp);
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mouseup", handleMouseUp);
window.removeEventListener("mousemove", handleMouseMove);
};
}, [handleMouseDown, handleMouseUp, handleMouseMove]);
return result;
};
const Test = () => {
const width = useMouseDelta(400);
return (
<div className="container" style={{ width }}>
<div className="resize" />
</div>
);
};
ReactDOM.render(<Test />, document.getElementById("root"));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.