在 React 中更改 mousedown 和 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 example
在此 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]
);
然后warning就不会出来了,但是你会看到它总是看到更新后的值,会引起额外的加法。
一个解决方案是在 useMouseDelta 中以不同的状态存储当前的 witdth,然后可以将其添加为依赖项:
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
,但您需要在两个事件之间存储 previousClientX
和 useRef()
and add just a change of clientX
。
- 你不需要存储
dragging
和 useState()
, 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"));
我想构建一个挂钩来更改 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 example
在此 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]
);
然后warning就不会出来了,但是你会看到它总是看到更新后的值,会引起额外的加法。
一个解决方案是在 useMouseDelta 中以不同的状态存储当前的 witdth,然后可以将其添加为依赖项:
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
,但您需要在两个事件之间存储previousClientX
和useRef()
and add just a change ofclientX
。 - 你不需要存储
dragging
和useState()
,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"));