【问题标题】:call addEventListener inside addEventListener callback function in react在 react 的 addEventListener 回调函数中调用 addEventListener
【发布时间】:2022-12-03 15:18:43
【问题描述】:

我是一名学习反应的学生。 我现在制作的组件有问题。 我想做的是一个类似于下面链接的滚动式选择器。
https://codepen.io/gnauhca/pen/JrdpZZ
在这个实现过程中,在eventListener中注册和移除eventListener时出现了问题。

import { useState, useRef, useEffect } from "react";

function Scroller() {
  const array = [170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180];
  const [dragEventHistory, setDragEventHistory] = useState([]);
  const [dragEventStart, setDragEventStart] = useState(null);

  const scrollerContainer = useRef();
  const scrollerList = useRef();

  useEffect(() => {
    const moveList = (clientY) => {
      const difference = Number(dragEventStart) - Number(clientY);
      scrollerList.current.style.transform = `translate3d(0, -${difference}px, 0)`;
    };

    const onMouseMoveEventHandler = (e) => {
      console.log(e);
      e.preventDefault();
      moveList(e.clientY);
    };

    const onMouseDownEventHandler = (e) => {
      console.log(e);
      e.preventDefault();
      setDragEventStart(e.clientY);
      scrollerContainer.current.addEventListener(
        "mousemove",
        onMouseMoveEventHandler
      );
    };

    const onMouseUpEventHandler = (e) => {
      console.log(e);
      e.preventDefault();
      scrollerContainer.current.removeEventListener(
        "mousemove",
        onMouseMoveEventHandler
      );
    };

    scrollerContainer.current.addEventListener(
      "mousedown",
      onMouseDownEventHandler
    );
    scrollerContainer.current.addEventListener(
      "mouseup",
      onMouseUpEventHandler
    );

    return () => {
      scrollerContainer.current.removeEventListener(
        "mousedown",
        onMouseDownEventHandler
      );
      scrollerContainer.current.removeEventListener(
        "mouseup",
        onMouseUpEventHandler
      );
    };
  }, []);

  return (
    <div
      className="scroller-container"
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        overflow: "hidden",
        width: 200,
        height: 250,
      }}
      ref={scrollerContainer}
      // onTouchStart={onTouchStartEventHandler}
      // onTouchEnd={onTouchEndEventHandler}
    >
      <ul className="scroller-list" ref={scrollerList}>
        {array.map((item, index) => {
          return (
            <li
              key={index}
              style={{
                width: 200,
                height: 50,
                display: "flex",
                justifyContent: "center",
                alignItems: "center",
                backgroundColor: "orange",
                fontSize: 30,
              }}
            >
              {item}
            </li>
          );
        })}
      </ul>
    </div>
  );
}

export default Scroller;

如果我不使用 useEffect 定义函数,根据 'dragEventStart' 状态的变化,注册为事件的回调函数无法在组件重新渲染过程中删除。
如果我在useEffect中定义函数,removeEventListener函数可以指向与addEventListener函数的回调相同的回调函数。但无法更改 dragEventStart 状态。但是,如果 dragEventStart 包含在 useEffect 的依赖项中,则与不使用 useEffect 时相同。

我怎么解决这个问题?我需要帮助!!

【问题讨论】:

  • 为什么你会在 React 中使用 addEventListener 而不是为此设计的道具?
  • @KonradLinkowski 你好! onMouseDown 和 onMouseUp 可以通过 react 道具注册,但我真正的问题是 onMouseMove 事件!如果 onMouseDown 为 ON,则应注册 onMouseMove 事件,如果 onMouseUp 为 ON,则应将其删除。 onMouseMove 事件的回调函数有一个状态(dragEventStart),所以问题出现了!

标签: javascript reactjs react-hooks


【解决方案1】:

与其声明 dragEventStart 一个 React 状态变量并依赖于适当的值封闭,我建议将它转换为一个可以随意改变的 React 引用。

此外,不是不断添加/删除事件侦听器,而是在组件安装时添加一次鼠标向下|向上|移动侦听器,并在返回的useEffect钩子清理函数中删除它们。

为了让用户体验更好一点,鼠标向上和移动事件监听器应该附加到 window 对象,这样如果用户在释放时不在 scrollerContainer 元素上,拖动就不会“卡住”鼠标按钮。

例子:

function Scroller() {
  ...
  const [dragEventHistory, setDragEventHistory] = useState([]);
  const dragEventStartRef = useRef(null);

  const scrollerContainer = useRef();
  const scrollerList = useRef();

  useEffect(() => {
    const moveList = (clientY) => {
      // Only handle move if drag is started
      if (dragEventStartRef.current) {
        const difference = Number(dragEventStartRef.current) - Number(clientY);
        scrollerList.current.style.transform = `translate3d(0, -${difference}px, 0)`;
      }
    };

    const onMouseMoveEventHandler = (e) => {
      e.preventDefault();
      moveList(e.clientY);
    };

    const onMouseDownEventHandler = (e) => {
      e.preventDefault();
      dragEventStartRef.current = e.clientY;
    };

    const onMouseUpEventHandler = (e) => {
      e.preventDefault();
      dragEventStartRef.current = null;
    };

    scrollerContainer.current.addEventListener(
      "mousedown",
      onMouseDownEventHandler
    );
    window.addEventListener("mouseup", onMouseUpEventHandler);
    window.addEventListener("mousemove", onMouseMoveEventHandler);

    return () => {
      scrollerContainer.current.removeEventListener(
        "mousedown",
        onMouseDownEventHandler
      );
      window.removeEventListener("mouseup", onMouseUpEventHandler);
      window.removeEventListener("mousemove", onMouseMoveEventHandler);
    };
  }, []);

  return (
    ...
  );
}

【讨论】:

  • 我认为这也让事情变得过于复杂——我怀疑如果处理程序一直在 div 上注册,会有很多性能问题,但是 mousedown/mouseup 处理程序更改了一个 ref-boxed 值,该值改变了鼠标移动处理程序是否做任何事...
  • 事实上,为了使其正常工作,鼠标向上处理程序需要位于 window 上,因此当鼠标不在元素上时松开按钮不会使其“卡住”滚动。 .
  • @AKX 啊,是的......这就是我的想法,而且我发现将鼠标向下/向上连接到特定的 DOMNode 很奇怪。
  • @AKX 你是对的,我需要再看看逻辑。无需持续添加/删除任何侦听器/处理程序,移动处理程序应仅在已启动拖动手势时运行任何逻辑。
  • 谢谢回答我的问题!!你们是我的英雄!!!!
【解决方案2】:

尝试了一些不同的东西,这是唯一的方法......谢谢O_o

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2022-07-12
  • 1970-01-01
  • 1970-01-01
  • 2021-04-03
  • 1970-01-01
  • 2022-11-29
  • 2020-12-16
  • 1970-01-01
相关资源
最近更新 更多