React Hooks onChange不接受输入

时间:2020-03-13 14:43:54

标签: reactjs mouseevent react-hooks

我有一个奇怪的错误,它只在某些时间发生-onChange会触发,但不会更改值。然后,如果我使用onChange函数在输入之外单击,然后在输入框中单击返回,则onChange函数将开始工作。

onChange函数如下:

const handleBarAmountChange = (event) => {
    let newWidthAmount = event.target.value / 10;
    setNewWidth(newWidthAmount);
    setNewBarAmount(event.target.value);
};

父div正在使用带有useRef的引用,该引用已传递给此函数:

import { useEffect, useState } from 'react';
const useMousePosition = (barRef, barInputRef, barContainerRef) => {
    const [ mouseIsDown, setMouseIsDown ] = useState(null);

    useEffect(() => {
        const setMouseDownEvent = (e) => {
            if (e.which == 1) {
                if (barContainerRef.current.contains(e.target) && !barInputRef.current.contains(e.target)) {
                    setMouseIsDown(e.clientX);
                } else if (!barInputRef.current.contains(e.target)) {
                    setMouseIsDown(null);
                }
            }
        };

        window.addEventListener('mousemove', setMouseDownEvent);
        return () => {
            window.removeEventListener('mousemove', setMouseDownEvent);
        };
    }, []);
    return { mouseIsDown };
};

onChange是否与eventListener发生冲突?

如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

React使用doc中的SyntheticEventEvent Pooling

事件池

SyntheticEvent已合并。这意味着在调用事件回调之后,SyntheticEvent对象将被重用,并且所有属性都将无效。这是出于性能原因。因此,您不能以异步方式访问事件。

您可以在事件上调用event.persist()或将值存储在新变量中,并按以下方式使用它:

const handleBarAmountChange = (event) => {
  // event.persist(); 
  // Or
  const { value } = event.target;

  let newWidthAmount = value / 10;
  setNewWidth(newWidthAmount);
  setNewBarAmount(value);
};

答案 1 :(得分:1)

存在一些语法错误和缺少挂钩依赖关系,这些都是导致错误的原因。但是,您可以通过一些调整来简化代码。

当使用依赖于其他状态的状态时,建议将其集中到一个对象中,并使用回调函数来同步更新它:setState(prevState => ({ ...prevState, example: "newValue" })。这类似于this.setState();在基于类的组件中的工作方式。通过使用单个对象并扩展其属性({ ...prevState }),我们可以通过重新定义其中一个属性({ ...prevState, newWidth: 0 })覆盖其属性之一。这样可以确保值彼此同步。

下面的示例遵循上述单个对象模式,其中newWidthnewBarAmountisDragging是单个对象(state)的属性。然后,该示例使用setState来同步更新/覆盖这些值。另外,参考文献也已删除,并允许将条形图拖动到窗口上方(如果您不希望这样做,则可以像以前一样将其限制在barContainerRef内)。当用户左键单击并按住该栏时,该示例还将检查state.isDragging布尔值。释放左键后,将禁用拖动。

这是一个可行的示例:

Edit Bar & Input Updates


components / Bar / index.js

import React, { useEffect, useState, useCallback } from "react";
import PropTypes from "prop-types";
import "./Bar.css";

function Bar({ barName, barAmount, colour, maxWidth }) {
  const [state, setState] = useState({
    newWidth: barAmount / 2,
    newBarAmount: barAmount,
    isDragging: false
  });

  // manual input changes
  const handleBarAmountChange = useCallback(
    ({ target: { value } }) => {
      setState(prevState => ({
        ...prevState,
        newWidth: value / 2,
        newBarAmount: value
      }));
    },
    []
  );

  // mouse move
  const handleMouseMove = useCallback(
    ({ clientX }) => {
      if (state.isDragging) {
        setState(prevState => ({
          ...prevState,
          newWidth: clientX > 0 ? clientX / 2 : 0,
          newBarAmount: clientX > 0 ? clientX : 0
        }));
      }
    },
    [state.isDragging]
  );

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

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

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

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

  return (
    <div className="barContainer">
      <div className="barName">{barName}</div>
      <div
        style={{ cursor: state.isDragging ? "grabbing" : "pointer" }}
        onMouseDown={handleMouseDown}
        className="bar"
      >
        <svg
          width={state.newWidth > maxWidth ? maxWidth : state.newWidth}
          height="40"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
          colour={colour}
        >
          <rect width={state.newWidth} height="40" fill={colour} />
        </svg>
      </div>
      <div className="barAmountUnit">£</div>
      <input
        className="barAmount"
        type="number"
        value={state.newBarAmount}
        onChange={handleBarAmountChange}
      />
    </div>
  );
}

// default props (will be overridden if defined)
Bar.defaultProps = {
  barAmount: 300,
  maxWidth: 600
};

// check that passed in props match patterns below
Bar.propTypes = {
  barName: PropTypes.string,
  barAmount: PropTypes.number,
  colour: PropTypes.string,
  maxWidth: PropTypes.number
};

export default Bar;