Я пытаюсь сделать компонент скользящим с помощью мыши. Но при отпускании мыши ссылка на один из элементов DOM становится пустой во время выполнения, и я не понимаю, почему.
Я создал Песочницу , чтобы проиллюстрировать проблему. (это не полная реализация. Как раз то, что нужно для эксперимента с проблемой).
Просто перетащите элементы и проверьте консоль. Вы увидите, что ссылка изначально установлена правильно, но при отпускании мыши ссылка исчезла.
Я не понимаю, когда это происходит и почему.
Вот полный Приложение. 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