0

我正在尝试使用鼠标使组件可滑动。但是当释放鼠标时,对 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

4

0 回答 0