为什么我的事件监听器回调没有使用正确的状态?

时间:2019-08-09 00:28:35

标签: javascript reactjs

我有一个使包裹元素可拖动的组件。开始拖动时,我将事件侦听器添加到窗口中以进行拖动移动和放置。

>>> torch.Size([100, 80, 4, 4])
>>> torch.Size([100, 40, 9, 9])
>>> torch.Size([100, 40, 7, 7])
>>> RuntimeError: expected padding to be a single integer value or a list of 2 values to match the convolution dimensions, but got padding=[0, 1, 0, 1]

使用这些回调:

    function start_drag({ x, y }) {
        window.addEventListener('mouseup', trigger_drop);
        window.addEventListener('mousemove', drag_move);
        dispatch({ type: DispatchActions.START, x: x, y: y });
    }

但是,这些回调使用自己的状态和调度版本。尝试了几件事之后,我无法解决此问题,此外,我对“ this”在此处的工作方式感到困惑。

我在React中工作,仅使用带有React Hooks的功能组件来获取状态等等。 对于许多其他stackoverflow问题,答案是使用绑定/箭头功能。如您所见,我将回调声明为箭头函数(不起作用),但是这使我感到有些奇怪。当我尝试绑定时,我发现我的功能组件中有 const trigger_drop = (e) => { //if (!dragging) { return; } end_drag(); if (deliver()) { if (props.onDrop) { props.onDrop(e); } } } const drag_move = (e) => { //if (!state.dragging) { return; } dispatch({ type: DispatchActions.MOVE, x: e.x, y: e.y }); if (props.onDragMove) { props.onDragMove(e); } } 。这可能是相关的。我对此的搜索仅给出了答案,要求将其绑定到React.Component类的构造函数中,在这里不起作用。

这是该模块的完整代码:

this === undefined

预期:在window.mouseup上,我希望回调trigger_drop访问正确的import React, { useContext, useEffect, useReducer } from 'react'; import { DragContext } from 'client/contexts/DragContext'; import dragtarget from './DragTarget.module.css'; const DispatchActions = { MOVE: 'move', START: 'start', STOP: 'stop' } function reducer(state, action) { switch(action.type) { case DispatchActions.MOVE: return { ...state, offset_x: action.x - (state.start_x + state.offset_x), offset_y: action.y - (state.start_y + state.offset_y) }; case DispatchActions.START: return { ...state, dragging: true, start_x: action.x, start_y: action.y, offset_x: 0, offset_y: 0 }; case DispatchActions.STOP: return { ...state, dragging: false }; default: return state; } } export default function DragTarget(props) { const { drag, deliver } = useContext(DragContext); const [state, dispatch] = useReducer(reducer, { dragging: false, start_x: 0, start_y: 0, offset_x: 0, offset_y: 0 }); useEffect(() => { return () => { end_drag(); }; }, []); function start_drag({ x, y }) { window.addEventListener('mouseup', trigger_drop); window.addEventListener('mousemove', drag_move); dispatch({ type: DispatchActions.START, x: x, y: y }); } function end_drag() { window.removeEventListener('mouseup', trigger_drop); window.removeEventListener('mousemove', drag_move); dispatch({ type: DispatchActions.STOP }); } const trigger_drag = (e) => { e.stopPropagation(); e.preventDefault(); if (drag(props.payload)) { start_drag({ x: e.x, y: e.y }); if (props.onDragStart) { props.onDragStart(); } } } const drag_move = (e) => { //if (!state.dragging) { return; } dispatch({ type: DispatchActions.MOVE, x: e.x, y: e.y }); if (props.onDragMove) { props.onDragMove(e); } } const trigger_drop = (e) => { //if (!state.dragging) { return; } end_drag(); if (deliver()) { if (props.onDrop) { props.onDrop(e); } } } return ( <div className={`${props.className} ${state.dragging ? dragtarget.dragging : null}`} style={{ transform: `translate(${state.offset_x}px, ${state.offset_y}px)` }} onMouseDown={trigger_drag}> {props.children} </div> ); } state.dragging。与window.mousemove上的drag_move相同。

当前:在window.mouseup上,回调trigger_drop的状态副本。拖动将返回dispatch(而不是引用具有false的正确副本),并且drag_move调度到具有未定义元素的状态(状态=== true)。

我希望我能清楚地解释这一点,否则请告诉我。预先感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

一种更简单的方法是摆脱调度异步操作,而利用可重用的组件将其自身状态作为单个对象处理,并进行同步setState回调更新。

例如,您可以使用两个事件侦听器和一个事件回调来简化逻辑:一个用于mouseup的事件监听器(单击鼠标)以保存元素,另一个用于mousemove的事件监听器(当按住鼠标单击并移动鼠标)来转换元素,最后,您可以使用元素的onMouseDown(鼠标单击释放)事件回调在其当前位置释放自己。

工作示例(此示例将styled-components用于更干净的代码,但您不必这样做):

Edit Drag and Drop Content Example


components / DragContainer / index.js

import styled from "styled-components";

export default styled.div.attrs(({ height, width, x, y }) => ({
  style: {
    transform: `translate(${x - width / 2}px, ${y - height / 2}px)`
  }
}))`
  cursor: grab;
  position: absolute;
  padding: 10px;
  border-radius: 4px;

  background-color: red;

  ${({ isDragging }) =>
    isDragging &&
    `
    opacity: 0.5;
    cursor: grabbing;
    z-index: 999999;
  `}
`;

components / Draggable / index.js

import React, {
  useState,
  useRef,
  useEffect,
  useCallback,
  useLayoutEffect
} from "react";
import PropTypes from "prop-types";
import DragContainer from "../DragContainer";

const Draggable = ({ children, position }) => {
  const dragRef = useRef(null);

  const [state, setState] = useState({
    isDragging: false,
    translateX: position.x,
    translateY: position.y,
    height: 0,
    width: 0
  });

  // mouse move
  const handleMouseMove = useCallback(
    ({ clientX, clientY }) => {
      if (state.isDragging) {
        setState(prevState => ({
          ...prevState,
          translateX: clientX,
          translateY: clientY
        }));
      }
    },
    [state.isDragging]
  );

  // mouse left click release
  const handleMouseUp = useCallback(() => {
    if (state.isDragging) {
      setState(prevState => ({
        ...prevState,
        isDragging: false
      }));
    }
  }, [state.isDragging]);

  // mouse left click hold
  const handleMouseDown = useCallback(() => {
    setState(prevState => ({
      ...prevState,
      isDragging: true
    }));
  }, []);

  // before painting, get element height and width
  // and zero out its position (this is
  // necessary to force the cursor to point at the
  // center of the element when dragging it)
  useLayoutEffect(() => {
    if (state.height < 1 && state.width < 1) {
      const { offsetHeight, offsetWidth } = dragRef.current;
      setState(prevState => ({
        ...prevState,
        translateX: position.x + offsetWidth / 2,
        translateY: position.y + offsetHeight / 2,
        height: offsetHeight,
        width: offsetWidth
      }));
    }
  }, [position, state, setState, dragRef]);

  useEffect(() => {
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("mouseup", handleMouseUp);

    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseup", handleMouseUp);
    };
  }, [handleMouseMove, handleMouseUp]);

  return (
    <DragContainer
      ref={dragRef}
      isDragging={state.isDragging}
      onMouseDown={handleMouseDown}
      x={state.translateX}
      y={state.translateY}
      height={state.height}
      width={state.width}
    >
      {children}
    </DragContainer>
  );
};

Draggable.propTypes = {
  children: PropTypes.node.isRequired,
  position: PropTypes.shape({
    x: PropTypes.number,
    y: PropTypes.number
  })
};

Draggable.defaultProps = {
  position: {
    x: 10,
    y: 10
  }
};

export default Draggable;

index.js

import React, { Fragment } from "react";
import { render } from "react-dom";
import { Draggable, Title } from "./components";

const App = () => (
  <Fragment>
    <Draggable position={{ x: 20, y: 20 }}>
      <Title>Hello</Title>
    </Draggable>
    <Draggable position={{ x: 140, y: 20 }}>
      <Title>Goodbye</Title>
    </Draggable>
  </Fragment>
);

render(<App />, document.getElementById("root"));

答案 1 :(得分:0)

我认为您可以尝试e => trigger_drop(e, props, dispatch)来获取正确的值和调度功能。