简体   繁体   English

在 React 中更改 mousedown 和 mousemove 的宽度

[英]Change width on mousedown and mousemove in React

I want to build a hook that changes the width of an element on mousemove and mousedown events.我想构建一个钩子,在mousemovemousedown事件上更改元素的宽度。

I'm using the following code (that is actually working):我正在使用以下代码(实际上是有效的):

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'));

However, I'm seeing this warning in ESLint:但是,我在 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

So I change it to:所以我把它改成:

setResult(r => r + (e.clientX - origin.current));

Now the drag is not working as desired anymore.现在拖动不再按预期工作。

An example can be found here: CodePen example可以在此处找到示例CodePen 示例

In this Effect you can add all the dependencies like this:在此 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]);

In the handleMouseMove it's a bit more tricky.在 handleMouseMove 中,它有点棘手。 It's ommited on purpose, so that it will only see the 'currentWidth' of the component and not the updates versions.它是故意省略的,因此它只会看到组件的“currentWidth”而不是更新版本。 The setResult can be used like this: setResult 可以这样使用:

  const handleMouseMove = useCallback((e) => {
      if (!dragging) {
        return;
      }
      setResult((result) => result + (e.clientX - origin.current));
    },
    [dragging]
  );

Then the warning will not come up, but you will see that it will always see the updated value and it will cause extra additions.然后警告不会出现,但您会看到它总是会看到更新的值,并且会导致额外的添加。

A solution would be to store the current witdth in a different state like this in useMouseDelta and then it can be added as a dependency:一个解决方案是在 useMouseDelta 中以不同的状态存储当前的宽度,然后可以将其添加为依赖项:

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;
};
  1. You don't need to store origin , but you need to store previousClientX with useRef() and add just a change of clientX between two events.您不需要存储origin ,但您需要使用useRef()存储previousClientX并仅在两个事件之间添加clientX的更改。
  2. You don't need to store dragging with useState() , useRef() is enough.您不需要使用useState()存储dragginguseRef()就足够了。
  3. You need to add all handlers to the dependencies list in your useEffect() .您需要将所有处理程序添加到useEffect()的依赖项列表。

The improved code :改进后的代码

CodePen 代码笔

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"));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM