javascript - Change width on mousedown and mousemove in React - Stack Overflow

I want to build a hook that changes the width of an element on mousemove and mousedown events.I'm

I want to build a hook that changes the width of an element on mousemove and mousedown events.

I'm using the following code (that is actually working):

import React, { useState, useCallback, useEffect, useRef } from "/react";
import ReactDOM from "/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:

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

I want to build a hook that changes the width of an element on mousemove and mousedown events.

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:

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

Share Improve this question edited Jul 23, 2021 at 7:24 Peter Bartels asked Jul 15, 2021 at 13:56 Peter BartelsPeter Bartels 1,5141 gold badge13 silver badges20 bronze badges 4
  • 1 When I include it to dependency array it seems to working fine. Isn't it? – Konstantin Modin Commented Jul 15, 2021 at 14:59
  • @KonstantinModin Yes that does work. But I still don't know why the setResult with a function inside isn't working, though. – Peter Bartels Commented Jul 15, 2021 at 15:34
  • When adding result it to the dependency array, it doesn't work properly anymore – Peter Bartels Commented Jul 22, 2021 at 13:01
  • Is it possible to see the eslint warnings inside codepen? – user16442705 Commented Jul 22, 2021 at 13:08
Add a ment  | 

2 Answers 2

Reset to default 4 +50
  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.
  2. You don't need to store dragging with useState(), useRef() is enough.
  3. You need to add all handlers to the dependencies list in your 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"));

In this Effect you can add all the dependencies like this:

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. It's ommited on purpose, so that it will only see the 'currentWidth' of the ponent and not the updates versions. The setResult can be used like this:

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

Then the warning will not e 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:

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;
};

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745268039a4619577.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信