[英].mouseup() and .mousedown() not working with .mousemove()
[英]Change width on mousedown and mousemove in React
我想構建一個鈎子,在mousemove
和mousedown
事件上更改元素的寬度。
我正在使用以下代碼(實際上是有效的):
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'));
但是,我在 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
所以我把它改成:
setResult(r => r + (e.clientX - origin.current));
現在拖動不再按預期工作。
可以在此處找到示例: CodePen 示例
在此 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]);
在 handleMouseMove 中,它有點棘手。 它是故意省略的,因此它只會看到組件的“currentWidth”而不是更新版本。 setResult 可以這樣使用:
const handleMouseMove = useCallback((e) => {
if (!dragging) {
return;
}
setResult((result) => result + (e.clientX - origin.current));
},
[dragging]
);
然后警告不會出現,但您會看到它總是會看到更新的值,並且會導致額外的添加。
一個解決方案是在 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
,但您需要使用useRef()
存儲previousClientX
並僅在兩個事件之間添加clientX
的更改。useState()
存儲dragging
, useRef()
就足夠了。useEffect()
的依賴項列表。改進后的代碼:
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.