我正在尝试使用鼠标使组件可滑动。但是当释放鼠标时,对 DOM 元素之一的引用在执行过程中变为空,我不明白为什么。
我创建了一个沙箱来说明这个问题。(这不是完整的实现。只是试验问题所需要的)。
只需拖动元素并检查控制台。您会看到 ref 最初设置正确,但是当释放鼠标时,ref 消失了。
我不明白这是什么时候发生的以及为什么。
这是完整的 App.js 文件:
import React from "react";
import styled from "styled-components";
const Container = styled.div`
width: 100%;
overflow: hidden;
`;
const Slides = styled.div`
position: relative;
cursor: pointer;
user-select: none;
white-space: nowrap;
`;
const Slide = styled.div`
width: ${props => props.size}%;
cursor: pointer;
height: 100%;
display: inline-block;
`;
const Carrousel = ({
children,
columnCount = 1,
onSlideChanged = () => {},
active = 0
}) => {
const [dragging, setDragging] = React.useState(false);
const initialMouseX = React.useRef(0); // Starting drag x coordinates
const initialLeft = React.useRef(0); // Left value when the drag started
const currentLeft = React.useRef(-active * (100 / children.length)); // Current left value
const sliderRef = React.createRef();
const slidesRef = React.createRef();
// This will directly manipulate the DOM for better performances
const setLeft = React.useCallback(
newLeft => {
currentLeft.current = newLeft;
if (!slidesRef.current) return;
slidesRef.current.style.left = currentLeft.current + "%";
},
[currentLeft, slidesRef]
);
// Starts the drag
const startDrag = ({ screenX }) => {
setDragging(true);
initialMouseX.current = screenX;
initialLeft.current = currentLeft.current;
};
// We need the references to be updated before being able to listen to move and mouseup events
React.useLayoutEffect(() => {
if (!dragging) return;
console.log("at last render, slidesRef was:", slidesRef.current);
const move = event => {
if (dragging && sliderRef.current) {
const width = sliderRef.current.getBoundingClientRect().width;
const newLeft =
initialLeft.current +
((event.screenX - initialMouseX.current) * 100) / width;
setLeft(newLeft);
event.preventDefault();
}
};
const stopDrag = () => {
setDragging(false);
console.log("when calling stopDrag, slidesRef was:", slidesRef.current);
};
document.addEventListener("mouseup", stopDrag);
document.addEventListener("mousemove", move);
return () => {
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", stopDrag);
};
}, [initialLeft, dragging, sliderRef, setLeft, slidesRef]);
return (
<Container ref={sliderRef}>
<Slides ref={slidesRef} onMouseDown={startDrag}>
{children.map((child, index) => (
<Slide key={index} size={100 / columnCount}>
{child}
</Slide>
))}
</Slides>
</Container>
);
};
const App = () => (
<Carrousel columnCount={2}>
{new Array(4).fill("").map((_, i) => (
<div key={i}>
<span>Test{i + 1}</span>
</div>
))}
</Carrousel>
);
export default App;
问题在这一部分内:
React.useLayoutEffect(() => {
if (!dragging) return;
console.log("at last render, slidesRef was:", slidesRef.current);
const move = event => {
if (dragging && sliderRef.current) {
const width = sliderRef.current.getBoundingClientRect().width;
const newLeft =
initialLeft.current +
((event.screenX - initialMouseX.current) * 100) / width;
setLeft(newLeft);
event.preventDefault();
}
};
const stopDrag = () => {
setDragging(false);
console.log("when calling stopDrag, slidesRef was:", slidesRef.current);
};
document.addEventListener("mouseup", stopDrag);
document.addEventListener("mousemove", move);
return () => {
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", stopDrag);
};
}, [initialLeft, dragging, sliderRef, setLeft, slidesRef]);
拖动时会得到:at last render, slidesRef was: <div ...>
,然后释放时:when calling stopDrag, slidesRef was: null