隐藏的输入不会触发React的onChange事件

时间:2019-09-15 10:27:17

标签: javascript reactjs typescript dom-events

我在React中的输入字段面临一个奇怪的问题。我知道以下事实:hidden输入不会触发inputchange事件。但是,即使我手动触发了它们,React的onChange事件仍然没有被调用。

我在这里同时触发changeinput事件,因为React的onChange实际上是input事件。当我在inputRefaddEventListener("change", () => { ... }))上设置事件侦听器以进行测试时,可以毫无问题地调用它。但是,事实证明React在拦截它方面存在一些问题。

这是我当前的代码:

const [fieldValue, setFieldValue] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);

const handleClick = useCallback((): void => {
    if (inputRef.current) {
        inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
        inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
    }

    setFieldValue(prev => prev + 1);
}, []);

JSX:

<input type="hidden" ref={inputRef} value={fieldValue} onChange={(e) => { console.log("React:onChange"); }} />
<button type="button" onClick={handleClick}>Hit it</button>

我在这里做错什么了吗?要正确触发React的onChange事件,我还需要做什么?

3 个答案:

答案 0 :(得分:1)

会触发when the user alters元素值的onChange事件-在您的情况下,您正在以编程方式更改输入值,因此onChange没有触发

onInput events fires,值更改时-无论如何更改。因此,您需要添加onInput甚至是侦听器,而不是onChange

<input type="hidden" ref={inputRef} value={fieldValue} onInput={(e) => { console.log("React:onChange"); }} />

这是一个有效的示例:Sandbox

答案 1 :(得分:1)

React中的事件由react-dom通过不同的插件处理。输入事件是通过 SimpleEventPlugin (请参见https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js

管理的

此插件获取一个事件并重新分配它,而不会过多干扰。因此,您可以将其作为本机事件进行分发,并且无需做太多更改即可将其作为 SyntheticEvent 触发。

更改事件由 ChangeEventPlugin 处理(请参见https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ChangeEventPlugin.js)。该插件具有以下目的:

  

此插件创建一个onChange事件,该事件规范化更改事件   跨表单元素。该事件在有可能发生时触发   更改元素的值而不会出现闪烁。

该插件会触发更改事件,但与原始更改事件不完全相同。例如,文本输入元素上的本机更改事件仅在模糊时触发。参见https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event

  

对于某些元素,包括,更改事件   在控件失去焦点之前不会触发。

但是使用React时,每次更改值都会触发不同的触发。为此,由具有以下限制的插件处理: 该事件将仅在以下输入类型上触发:

  color: true,
  date: true,
  datetime: true,
  'datetime-local': true,
  email: true,
  month: true,
  number: true,
  password: true,
  range: true,
  search: true,
  tel: true,
  text: true,
  time: true,
  url: true,
  week: true

因此,在隐藏的输入上,react停止调度。

另一个限制是,仅当输入的值实际更改时才调度事件。见

 if (updateValueIfChanged(targetNode)) {
    return targetInst;
  }

因此,即使在受支持的输入上,您的调度也不会通过插件执行。您可以通过操作用于获取输入值的方法来在此片段中看到它,您可以设法分配该事件。

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();

  }

  componentDidMount() {
  //_valueTracker.getValue is used for comparing the values of the input. If you write fake value in the input it'll dispatch, anything else won't
    document.getElementsByTagName('INPUT')[0]._valueTracker.getValue = () => {
      return 'fake value'
    }
  }

  render() {

    const handleClick = () => {
      if (this.myRef) {

        this.myRef.current.dispatchEvent(new Event("change", {
          bubbles: true
        }));


      }
    };

    return <div > < input type = "text"
    ref = {
      this.myRef
    }
    onChange = {
      (e) => {
        console.log("React:onChange");
      }
    }
    /><button type="button"  onClick={handleClick}>Hit it</button > < /div>

  }
}

ReactDOM.render( <
  Hello name = "World" / > ,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="container">

</div>

因此对于隐藏的输入,无法使其正常工作(请注意,它符合标准)。对于文本(或其他受支持的输入),您可以,但是您需要或多或少地修改将事件之前的值与事件之后的值进行比较的方法。

答案 2 :(得分:0)

我认为您可能会以错误的方式进行处理。当您单击按钮时,您希望状态更新,并且新状态会反映在隐藏字段中。而不是检查隐藏字段中的更改,您应该检查状态的更改,您可以使用useEffect轻松地进行更改。这样,您就可以省去引用,而不会使事情复杂化。

function App() {
  const [fieldValue, setFieldValue] = useState(0);

  function handleClick() {
    setFieldValue(prev => prev + 1);
  }

  useEffect(() => {
     console.log('React:changed');
  }, [fieldValue]);

  return (
    <>
      <input type="hidden" value={fieldValue} />
      <button type="button" onClick={handleClick}>
        Hit it
      </button>
    </>
  );
}

Sandbox